From 4c3fcf0e367963a6c6c85ac4514cdc3501750334 Mon Sep 17 00:00:00 2001 From: Max Leiter Date: Thu, 2 Jun 2022 16:10:45 -0700 Subject: [PATCH] progress --- src/models/network.ts | 3 + src/plugins/middleware/chat-history.ts | 101 +++++++++++++++++++++++++ src/plugins/middleware/middleware.d.ts | 23 ++++++ src/types/modules/irc-framework.d.ts | 14 +++- 4 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 src/plugins/middleware/chat-history.ts create mode 100644 src/plugins/middleware/middleware.d.ts diff --git a/src/models/network.ts b/src/models/network.ts index ed5038f5..5bc5bfcd 100644 --- a/src/models/network.ts +++ b/src/models/network.ts @@ -9,6 +9,7 @@ import Config, {WebIRC} from "../config"; import STSPolicies from "../plugins/sts"; import ClientCertificate, {ClientCertificateType} from "../plugins/clientCertificate"; import Client from "../client"; +import ChatHistoryMiddleware from "../plugins/middleware/chat-history"; /** * List of keys which should be sent to the client by default. @@ -284,6 +285,8 @@ class Network { // TODO: this type should be set after setIrcFrameworkOptions }) as NetworkWithIrcFramework["irc"]; + this.irc.use(ChatHistoryMiddleware()); + this.setIrcFrameworkOptions(client); this.irc.requestCap([ diff --git a/src/plugins/middleware/chat-history.ts b/src/plugins/middleware/chat-history.ts new file mode 100644 index 00000000..88443004 --- /dev/null +++ b/src/plugins/middleware/chat-history.ts @@ -0,0 +1,101 @@ +import EventEmitter from "events"; +import { Client, IRCMiddleware } from "irc-framework"; +import { isDate } from "lodash"; + + + +const initChatHistoryCommands = (irc: Client) => { + const isEnabled = () => irc.network.supports('draft/chathistory') || irc.network.supports('chathistory'); + const messageLimit = () => + irc.network.options["DRAFT/CHATHISTORY"] + || irc.network.options.CHATHISTORY || 100; + + const getTimestamp = (ts: string | Date) => { + if (isDate(ts)) { + return "timestamp=" + ts.toISOString(); + } + + return ts; + } + + const commands: typeof Client.prototype.chatHistory = { + async latest(target: string, timestamp = "*", limit = messageLimit()) { + // CHATHISTORY LATEST #channel * 50 + if (!isEnabled()) { + return; + } + + irc.raw(`CHATHISTORY LATEST ${target} ${getTimestamp(timestamp)} ${limit}`); + + } + }; + + irc.chatHistory = commands; +} + +const initCallbacks = (irc: Client) => { + const batches: Map Promise)> = new Map(); + + irc.chatHistory.batches = batches; + + irc.chatHistory.addCallback = (id, callback) => { + batches.set(id, callback); + } + + irc.chatHistory.runCallback = async (id: number) => { + if (batches.has(id)) { + // TODO: investigate typing + await batches.get(id)!(); + } + } +} + +type BatchResponse = { + id: number, + type: 'chathistory', + params: string[], + commands: string[], +} + +const initListeners = (irc: Client) => { + irc.on("batch start chathistory", (data: BatchResponse) => { + irc.chatHistory.batches.set(data.id, new Promise((resolve, reject) => { + irc.chatHistory.addCallback(data.id, () => { + resolve(); + }); + } + }) + + irc.on("batch end chathistory", (data: BatchResponse) => { + if (!irc.chatHistory) { + return; + } + + // irc.chatHistory.runCallbacks(data.id); + }); +} + +function ChatHistoryMiddleware(): IRCMiddleware { + return function (irc, raw_events, parsed_events) { + irc.requestCap(['draft/chathistory']); + + irc.chatHistory.batches = new Map>(); + + + irc.on("batch end chathistory", (data: BatchResponse) => { + irc.chatHistory.listener.emit("chathistory", data); + }); + + initCallbacks(irc); + initListeners(irc); + initChatHistoryCommands(irc); + // initChatHistoryListeners(client); + parsed_events.use(theMiddleware); + } + + function theMiddleware(command: string, event: any, client: Client, next: () => void) { + next(); + } +} + +export default ChatHistoryMiddleware; diff --git a/src/plugins/middleware/middleware.d.ts b/src/plugins/middleware/middleware.d.ts new file mode 100644 index 00000000..fd657f63 --- /dev/null +++ b/src/plugins/middleware/middleware.d.ts @@ -0,0 +1,23 @@ +import EventEmitter from "events"; + +// import { Client } from "irc-framework" +type Timestamp = `timestamp=${string}` | `msgid=${string}` | Date; +type LatestTimestamp = "*" | Timestamp; + +declare module "irc-framework" { + interface Client { + chatHistory: { + batches: Map>; + addCallback: (id: number, callback: (data?: void | PromiseLike) => Promise) => void; + async runCallback(id: number): void; + + async before(target: string, timestamp: Timestamp, limit: number): Promise; + async after(target: string, timestamp: Timestamp, limit: number): Promise; + async latest(target: string, timestamp: LatestTimestamp, limit: number): Promise; + async around(target: string, timestamp: Timestamp, limit: number): Promise; + async between(target: string, timestamp1: Timestamp, timestamp2: Timestamp, limit: number): Promise; + async targets(timestamp1: Timestamp, timestamp2: Timestamp, limit: number): Promise; + async targets(target: string, timestamp: Timestamp): Promise; + } + } +} diff --git a/src/types/modules/irc-framework.d.ts b/src/types/modules/irc-framework.d.ts index 3aca2482..5d7a846b 100644 --- a/src/types/modules/irc-framework.d.ts +++ b/src/types/modules/irc-framework.d.ts @@ -5,7 +5,7 @@ // https://raw.githubusercontent.com/eternagame/HTML-Chat/vue-rewrite/src/app/types/modules/irc-framework/irc-framework.d.ts // TODO: Fix this declare module "irc-framework" { - import {EventEmitter} from "eventemitter3"; + import { EventEmitter } from "eventemitter3"; // import { DuplexStream } from 'stream'; import Connection from "irc-framework/src/transports/websocket"; @@ -23,6 +23,8 @@ declare module "irc-framework" { end: () => void; }; + export type IRCMiddleware = (client: Client, raw_events: Array, parsed_events: any) => void; + export interface MessageEventArgs { account?: any; group?: any; @@ -31,7 +33,7 @@ declare module "irc-framework" { message: string; nick: string; reply: (message: string) => void; - tags: {[key: string]: string}; + tags: { [key: string]: string }; target: string; time?: any; type: "privmsg" | "action"; // TODO @@ -111,6 +113,8 @@ declare module "irc-framework" { PREFIX: any; CHANMODES: string; NICKLEN: string; + 'DRAFT/CHATHISTORY': number; + CHATHISTORY: number; }; cap: { isEnabled: (cap: string) => boolean; @@ -141,7 +145,9 @@ declare module "irc-framework" { // TODO /** Request */ requestCap(capability: string[]): void; - use(a: any): any; + + + use(a: IRCMiddleware): any; connect(connect_options?: Record): void; @@ -227,7 +233,7 @@ declare module "irc-framework" { match_regex: string, cb: (event: Event) => any, message_type: string - ): {stop: () => void}; + ): { stop: () => void }; matchNotice(match_regex: string, cb: (event: Event) => any): void;