Skip to content

Commit c6e4da4

Browse files
committed
feat(plugins): add some plugins
1 parent c610bfb commit c6e4da4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2836
-0
lines changed

client.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import * as Core from "./core/mod.ts";
2+
import * as Action from "./plugins/action.ts";
3+
import * as Clientinfo from "./plugins/clientinfo.ts";
4+
import * as Ctcp from "./plugins/ctcp.ts";
5+
import * as Debug from "./plugins/debug.ts";
6+
import * as Error from "./plugins/error.ts";
7+
import * as Invite from "./plugins/invite.ts";
8+
import * as Join from "./plugins/join.ts";
9+
import * as Kick from "./plugins/kick.ts";
10+
import * as Kill from "./plugins/kill.ts";
11+
import * as Motd from "./plugins/motd.ts";
12+
import * as Msg from "./plugins/msg.ts";
13+
import * as Nick from "./plugins/nick.ts";
14+
import * as Notice from "./plugins/notice.ts";
15+
import * as Oper from "./plugins/oper.ts";
16+
import * as Part from "./plugins/part.ts";
17+
import * as Ping from "./plugins/ping.ts";
18+
import * as Quit from "./plugins/quit.ts";
19+
import * as Register from "./plugins/register.ts";
20+
import * as Time from "./plugins/time.ts";
21+
import * as Topic from "./plugins/topic.ts";
22+
import * as Version from "./plugins/version.ts";
23+
import * as Whois from "./plugins/whois.ts";
24+
25+
export type Options =
26+
& Core.Options
27+
& Debug.Options
28+
& Invite.Options
29+
& Nick.Options
30+
& Ping.Options
31+
& Register.Options
32+
& Time.Options
33+
& Version.Options;
34+
35+
export type Commands =
36+
& Action.Commands
37+
& Clientinfo.Commands
38+
& Ctcp.Commands
39+
& Invite.Commands
40+
& Join.Commands
41+
& Kick.Commands
42+
& Kill.Commands
43+
& Motd.Commands
44+
& Msg.Commands
45+
& Nick.Commands
46+
& Notice.Commands
47+
& Oper.Commands
48+
& Part.Commands
49+
& Ping.Commands
50+
& Quit.Commands
51+
& Time.Commands
52+
& Topic.Commands
53+
& Version.Commands
54+
& Whois.Commands;
55+
56+
export type Events =
57+
& Core.Events
58+
& Action.Events
59+
& Clientinfo.Events
60+
& Ctcp.Events
61+
& Invite.Events
62+
& Join.Events
63+
& Kick.Events
64+
& Kill.Events
65+
& Motd.Events
66+
& Msg.Events
67+
& Nick.Events
68+
& Notice.Events
69+
& Part.Events
70+
& Ping.Events
71+
& Quit.Events
72+
& Register.Events
73+
& Time.Events
74+
& Topic.Events
75+
& Version.Events
76+
& Whois.Events;
77+
78+
export type State =
79+
& Nick.State
80+
& Register.State;
81+
82+
const plugins = [
83+
Action.plugin,
84+
Clientinfo.plugin,
85+
Ctcp.plugin,
86+
Debug.plugin,
87+
Error.plugin,
88+
Invite.plugin,
89+
Join.plugin,
90+
Kick.plugin,
91+
Kill.plugin,
92+
Motd.plugin,
93+
Msg.plugin,
94+
Nick.plugin,
95+
Notice.plugin,
96+
Oper.plugin,
97+
Part.plugin,
98+
Ping.plugin,
99+
Quit.plugin,
100+
Register.plugin,
101+
Time.plugin,
102+
Topic.plugin,
103+
Version.plugin,
104+
Whois.plugin,
105+
];
106+
107+
export interface Client extends Commands {
108+
readonly options: Readonly<Options>;
109+
readonly state: Readonly<State>;
110+
}
111+
112+
export class Client extends Core.Client<Events> {
113+
constructor(options: Options) {
114+
super(options, plugins);
115+
}
116+
}

mod.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { Client, Options } from "./client.ts";

plugins/action.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type { ExtendedClient, UserMask } from "../core/mod.ts";
2+
import { createPlugin } from "../core/mod.ts";
3+
import type { CtcpPluginParams } from "./ctcp.ts";
4+
5+
export interface Commands {
6+
action(target: string, action: string): void;
7+
me(target: string, action: string): void;
8+
}
9+
10+
export interface Events {
11+
"ctcp_action": CtcpAction;
12+
}
13+
14+
export interface CtcpAction {
15+
origin: UserMask;
16+
target: string;
17+
text: string;
18+
}
19+
20+
export interface ActionPluginParams {
21+
commands: Commands;
22+
events: Events;
23+
}
24+
25+
function commands(
26+
client: ExtendedClient<ActionPluginParams & CtcpPluginParams>,
27+
) {
28+
client.action = client.me = (target, action) => {
29+
client.ctcp(target, "ACTION", action);
30+
};
31+
}
32+
33+
function events(client: ExtendedClient<ActionPluginParams & CtcpPluginParams>) {
34+
client.on("raw:ctcp", (msg) => {
35+
if (msg.command !== "ACTION") {
36+
return;
37+
}
38+
39+
const { origin, target } = msg;
40+
41+
client.emit("ctcp_action", {
42+
origin,
43+
target,
44+
text: msg.param!,
45+
});
46+
});
47+
}
48+
49+
export const plugin = createPlugin(commands, events);

plugins/action_test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { assertEquals } from "../core/test_deps.ts";
2+
import { arrange } from "../core/test_helpers.ts";
3+
import { plugin as action } from "./action.ts";
4+
import { plugin as ctcp } from "./ctcp.ts";
5+
6+
Deno.test("action commands", async () => {
7+
const { server, client, sanitize } = arrange([ctcp, action], {});
8+
9+
server.listen();
10+
client.connect(server.host, server.port);
11+
await client.once("connected");
12+
13+
client.action("#channel", "says hello");
14+
const raw1 = await server.once("PRIVMSG");
15+
assertEquals(raw1, "PRIVMSG #channel :\u0001ACTION says hello\u0001");
16+
17+
client.me("#channel", "says hello");
18+
const raw2 = await server.once("PRIVMSG");
19+
assertEquals(raw2, "PRIVMSG #channel :\u0001ACTION says hello\u0001");
20+
21+
await sanitize();
22+
});
23+
24+
Deno.test("action events", async () => {
25+
const { server, client, sanitize } = arrange([ctcp, action], {});
26+
27+
server.listen();
28+
client.connect(server.host, server.port);
29+
await server.waitClient();
30+
31+
server.send(
32+
":nick!user@host PRIVMSG #channel :\u0001ACTION says hello\u0001",
33+
);
34+
const msg = await client.once("ctcp_action");
35+
assertEquals(msg, {
36+
origin: { nick: "nick", username: "user", userhost: "host" },
37+
target: "#channel",
38+
text: "says hello",
39+
});
40+
41+
await sanitize();
42+
});

plugins/clientinfo.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import type { ExtendedClient, UserMask } from "../core/mod.ts";
2+
import { createPlugin } from "../core/mod.ts";
3+
import type { AnyCtcpCommand, CtcpPluginParams } from "./ctcp.ts";
4+
import { createCtcp } from "./ctcp.ts";
5+
6+
export interface Options {
7+
replies?: {
8+
clientinfo?: boolean;
9+
};
10+
}
11+
12+
export interface Commands {
13+
clientinfo(target: string): void;
14+
}
15+
16+
export interface Events {
17+
"ctcp_clientinfo": CtcpClientinfo;
18+
"ctcp_clientinfo_reply": CtcpClientinfoReply;
19+
}
20+
21+
export interface CtcpClientinfo {
22+
origin: UserMask;
23+
target: string;
24+
}
25+
26+
export interface CtcpClientinfoReply {
27+
origin: UserMask;
28+
target: string;
29+
supported: AnyCtcpCommand[];
30+
}
31+
32+
export interface ClientinfoPluginParams {
33+
commands: Commands;
34+
events: Events;
35+
options: Options;
36+
}
37+
38+
function options(client: ExtendedClient<ClientinfoPluginParams>) {
39+
client.options.replies ??= {};
40+
client.options.replies.clientinfo ??= true;
41+
}
42+
43+
function commands(
44+
client: ExtendedClient<ClientinfoPluginParams & CtcpPluginParams>,
45+
) {
46+
client.clientinfo = (target) => {
47+
client.ctcp(target, "CLIENTINFO");
48+
};
49+
}
50+
51+
function events(
52+
client: ExtendedClient<ClientinfoPluginParams & CtcpPluginParams>,
53+
) {
54+
client.on("raw:ctcp", (msg) => {
55+
if (msg.command !== "CLIENTINFO") {
56+
return;
57+
}
58+
59+
const { origin, target, param } = msg;
60+
61+
switch (msg.type) {
62+
case "query":
63+
client.emit("ctcp_clientinfo", {
64+
origin,
65+
target,
66+
});
67+
break;
68+
69+
case "reply":
70+
const supported = (param?.split(" ") ?? []) as AnyCtcpCommand[];
71+
client.emit("ctcp_clientinfo_reply", {
72+
origin,
73+
target,
74+
supported,
75+
});
76+
break;
77+
}
78+
});
79+
}
80+
81+
function replies(client: ExtendedClient<ClientinfoPluginParams>) {
82+
if (client.options.replies?.clientinfo === false) {
83+
return;
84+
}
85+
86+
client.on("ctcp_clientinfo", (msg) => {
87+
client.send(
88+
"NOTICE",
89+
msg.origin.nick,
90+
createCtcp("CLIENTINFO", "PING TIME VERSION"),
91+
);
92+
});
93+
}
94+
95+
export const plugin = createPlugin(options, commands, events, replies);

plugins/clientinfo_test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { assertEquals } from "../core/test_deps.ts";
2+
import { arrange } from "../core/test_helpers.ts";
3+
import { plugin as clientinfo } from "./clientinfo.ts";
4+
import { plugin as ctcp } from "./ctcp.ts";
5+
6+
Deno.test("clientinfo commands", async () => {
7+
const { server, client, sanitize } = arrange([ctcp, clientinfo], {});
8+
9+
server.listen();
10+
client.connect(server.host, server.port);
11+
await client.once("connected");
12+
13+
client.clientinfo("#channel");
14+
const raw = await server.once("PRIVMSG");
15+
assertEquals(raw, "PRIVMSG #channel \u0001CLIENTINFO\u0001");
16+
17+
await sanitize();
18+
});
19+
20+
Deno.test("clientinfo events", async () => {
21+
const { server, client, sanitize } = arrange([ctcp, clientinfo], {});
22+
23+
server.listen();
24+
client.connect(server.host, server.port);
25+
await server.waitClient();
26+
27+
server.send(":nick!user@host PRIVMSG #channel :\u0001CLIENTINFO\u0001");
28+
const msg1 = await client.once("ctcp_clientinfo");
29+
assertEquals(msg1, {
30+
origin: { nick: "nick", username: "user", userhost: "host" },
31+
target: "#channel",
32+
});
33+
34+
server.send(
35+
":nick2!user@host NOTICE nick :\u0001CLIENTINFO PING TIME VERSION\u0001",
36+
);
37+
const msg2 = await client.once("ctcp_clientinfo_reply");
38+
assertEquals(msg2, {
39+
origin: { nick: "nick2", username: "user", userhost: "host" },
40+
target: "nick",
41+
supported: ["PING", "TIME", "VERSION"],
42+
});
43+
44+
await sanitize();
45+
});
46+
47+
Deno.test("clientinfo replies", async () => {
48+
const { server, client, sanitize } = arrange([ctcp, clientinfo], {});
49+
50+
server.listen();
51+
client.connect(server.host, server.port);
52+
await server.waitClient();
53+
54+
server.send(":nick2!user@host PRIVMSG nick :\u0001CLIENTINFO\u0001");
55+
const raw1 = await server.once("NOTICE");
56+
assertEquals(raw1, "NOTICE nick2 :\u0001CLIENTINFO PING TIME VERSION\u0001");
57+
58+
await sanitize();
59+
});

0 commit comments

Comments
 (0)