refactor: enhance event handling in IRC, Slack, and Telegram clients

This commit is contained in:
Flummi 2025-03-18 10:47:58 +01:00
parent 3e1cfe18f9
commit ca114f677e
4 changed files with 70 additions and 36 deletions

View File

@ -29,6 +29,11 @@ interface ParsedCommand {
params: string[]; params: string[];
} }
interface IRCEvents {
data: [string | [string, any]];
error: [string];
}
const colors = { const colors = {
white: "00", black: "01", navy: "02", green: "03", red: "04", white: "00", black: "01", navy: "02", green: "03", red: "04",
brown: "05", purple: "06", orange: "07", yellow: "08", brown: "05", purple: "06", orange: "07", yellow: "08",
@ -59,6 +64,14 @@ export default class irc extends EventEmitter {
user: Map<string, { account?: string; cached: number }>; user: Map<string, { account?: string; cached: number }>;
}; };
emit<K extends keyof IRCEvents>(event: K, ...args: IRCEvents[K]): boolean {
return super.emit(event, ...args);
}
on<K extends keyof IRCEvents>(event: K, listener: (...args: IRCEvents[K]) => void): this {
return super.on(event, listener);
}
constructor(options: IRCOptions) { constructor(options: IRCOptions) {
super(); super();
this.options = { this.options = {
@ -102,38 +115,29 @@ export default class irc extends EventEmitter {
this.connect(); this.connect();
} }
private createSocket(): net.Socket | tls.TLSSocket {
return this.options.ssl
? tls.connect({
host: this.options.host,
port: this.options.port,
rejectUnauthorized: !this.options.selfSigned,
})
: net.connect({
host: this.options.host,
port: this.options.port,
});
}
connect(reconnect: boolean = false): void { connect(reconnect: boolean = false): void {
if(reconnect) if(reconnect)
this.socket = undefined; this.socket = undefined;
if(this.options.ssl) { this.socket = this.createSocket();
this.socket = tls.connect({ this.socket.on("data", (msg: string) => this.handleData(msg));
host: this.options.host, this.socket.on("end", () => this.handleDisconnect());
port: this.options.port, this.socket.on("error", (err: Error) => this.handleError(err));
rejectUnauthorized: !this.options.selfSigned,
}, () => this.handleConnection());
}
else {
this.socket = net.connect({
host: this.options.host,
port: this.options.port,
}, () => this.handleConnection());
}
if(!this.socket)
throw new Error("Socket konnte nicht initialisiert werden.");
this.socket.setEncoding("utf-8"); this.socket.setEncoding("utf-8");
this.socket.on("data", (msg: string) => { this.handleConnection();
console.log("Received data:", msg);
this.handleData(msg);
});
this.socket.on("end", () => {
this.handleDisconnect();
});
this.socket.on("error", (err: Error) => {
this.handleError(err);
});
} }
private handleConnection(): void { private handleConnection(): void {

View File

@ -40,12 +40,27 @@ interface SlackRTMStartResponse {
description?: string; description?: string;
} }
interface SlackEvents {
data: [string | [string, any]];
error: [string];
message: [SlackMessage];
}
export default class slack extends EventEmitter { export default class slack extends EventEmitter {
private options: Required<SlackOptions>; private options: Required<SlackOptions>;
private token: string; private token: string;
private api: string = "https://slack.com/api"; private api: string = "https://slack.com/api";
private interval: NodeJS.Timeout | null = null; private interval: NodeJS.Timeout | null = null;
private server: ServerInfo; private server: ServerInfo;
private reconnectAttempts = 0;
emit<K extends keyof SlackEvents>(event: K, ...args: SlackEvents[K]): boolean {
return super.emit(event, ...args);
}
on<K extends keyof SlackEvents>(event: K, listener: (...args: SlackEvents[K]) => void): this {
return super.on(event, listener);
}
constructor(options: SlackOptions) { constructor(options: SlackOptions) {
super(); super();
@ -92,6 +107,7 @@ export default class slack extends EventEmitter {
if(res.url) { if(res.url) {
this.server.wss.url = url.parse(res.url); this.server.wss.url = url.parse(res.url);
this.reconnectAttempts = 0;
this.initializeWebSocket(); this.initializeWebSocket();
} }
else else
@ -128,7 +144,7 @@ export default class slack extends EventEmitter {
if(!this.server.wss.socket) if(!this.server.wss.socket)
return; return;
this.interval = setInterval(async () => await this.ping(), 30000); this.interval = setInterval(async () => await this.ping(), 3e4);
this.server.wss.socket.on("data", async (data: Buffer) => { this.server.wss.socket.on("data", async (data: Buffer) => {
try { try {
@ -158,12 +174,15 @@ export default class slack extends EventEmitter {
} }
async reconnect(): Promise<void> { async reconnect(): Promise<void> {
this.server.wss.url = null; if(this.reconnectAttempts >= 5) {
this.server.wss.socket = null; this.emit("data", ["error", "Too many reconnect attempts"]);
if(this.interval) return;
clearInterval(this.interval); }
this.emit("data", ["info", "reconnecting slack"]); this.reconnectAttempts++;
await this.connect(); setTimeout(async () => {
this.emit("data", ["info", "Reconnecting to Slack"]);
await this.connect();
}, this.reconnectAttempts * 1e3);
} }
async getChannel(channelId: string): Promise<string | undefined> { async getChannel(channelId: string): Promise<string | undefined> {
@ -247,8 +266,7 @@ export default class slack extends EventEmitter {
private parseData(data: Buffer): SlackMessage | undefined { private parseData(data: Buffer): SlackMessage | undefined {
try { try {
const json = JSON.parse(data.toString()); return JSON.parse(data.toString()) as SlackMessage;
return json;
} }
catch(err: any) { catch(err: any) {
this.emit("data", ["error", "failed to parse data"]); this.emit("data", ["error", "failed to parse data"]);

View File

@ -50,6 +50,11 @@ interface TelegramUpdate {
inline_query?: any; inline_query?: any;
} }
interface TelegramEvents {
data: [string | [string, any]];
error: [string];
}
export default class tg extends EventEmitter { export default class tg extends EventEmitter {
private options: Required<TelegramOptions>; private options: Required<TelegramOptions>;
private token: string; private token: string;
@ -64,6 +69,14 @@ export default class tg extends EventEmitter {
me: {}, me: {},
}; };
emit<K extends keyof TelegramEvents>(event: K, ...args: TelegramEvents[K]): boolean {
return super.emit(event, ...args);
}
on<K extends keyof TelegramEvents>(event: K, listener: (...args: TelegramEvents[K]) => void): this {
return super.on(event, listener);
}
constructor(options: TelegramOptions) { constructor(options: TelegramOptions) {
super(); super();
this.options = { this.options = {

1
src/types.d.ts vendored
View File

@ -11,7 +11,6 @@ export interface Bot {
server: { server: {
channel: Map<string, string[]>; channel: Map<string, string[]>;
motd: string; motd: string;
//user: Map<string, any>;
user: { user: {
[channel: string]: any [channel: string]: any
}; };