Compare commits

...

2 Commits

Author SHA1 Message Date
MrMasrozYTLIVE
623645090d Got rid of Export default's because they're bad bad 2024-06-16 12:19:18 +03:00
MrMasrozYTLIVE
d7ad22b115 Remade lib to TypeScript 2024-06-16 11:57:11 +03:00
14 changed files with 339 additions and 281 deletions

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
.idea/
package-lock.json
node_modules
docs
docs
storage

View File

@ -1,51 +0,0 @@
{
"tags": {
"allowUnknownTags": true
},
"source": {
"include": ["./src"],
"includePattern": ".js$",
"excludePattern": "(node_modules/|docs)"
},
"plugins": [
"plugins/markdown"
],
"opts": {
"template": "node_modules/docdash",
"encoding": "utf8",
"destination": "docs/",
"recurse": true,
"verbose": true
},
"markdown": {
"parser": "gfm",
"hardwrap": true,
"idInHeadings": true
},
"templates": {
"cleverLinks": false,
"monospaceLinks": false,
"default": {
"outputSourceFiles": true,
"includeDate": false,
"useLongnameInNav": true
}
},
"docdash": {
"static": true,
"sort": true,
"search": true,
"collapse": true,
"typedefs": true,
"removeQuotes": "none",
"wrap": true,
"menu": {
"Git Repository": {
"href":"https://git.ssh.surf/mitask/linkup-bot-lib",
"target":"_blank",
"class":"menu-item",
"id":"gitrepo_link"
}
}
}
}

View File

@ -1,13 +1,16 @@
{
"name": "linkup-bot-lib",
"version": "1.0.0",
"main": "src/Client.js",
"version": "1.1.0",
"main": "lib/Client.js",
"types": "lib/*.ts",
"scripts": {
"generate-docs": "jsdoc --configure jsdoc.json"
"prepublishOnly": "typedoc",
"build": "typedoc"
},
"keywords": [],
"author": "",
"type": "module",
"keywords": [
"linkup"
],
"author": "LinkUp Team",
"license": "ISC",
"description": "",
"dependencies": {
@ -18,7 +21,8 @@
"serve-drive": "^5.0.8"
},
"devDependencies": {
"docdash": "^2.0.2",
"jsdoc": "^4.0.3"
"@types/b4a": "^1.6.4",
"@types/node": "^20.14.2",
"typedoc-plugin-merge-modules": "^5.1.0"
}
}

View File

@ -1,34 +1,46 @@
import path from 'path';
import Hyperswarm from 'hyperswarm';
import EventEmitter from 'node:events';
import b4a from "b4a";
import TextMessage from "./message/TextMessage.js";
import FileMessage from "./message/FileMessage.js";
import AudioMessage from "./message/AudioMessage.js";
import Message from "./message/Message.js";
import IconMessage from "./message/IconMessage.js";
import Hyperswarm, { PeerDiscovery } from 'hyperswarm';
import {TextMessage} from "./message/TextMessage";
import {FileMessage} from "./message/FileMessage";
import {AudioMessage} from "./message/AudioMessage";
import {Message} from "./message/Message";
import Corestore from 'corestore';
import Hyperdrive from 'hyperdrive';
import fs from 'fs';
// @ts-ignore
import ServeDrive from 'serve-drive';
import {IconMessage} from "./message/IconMessage";
import {TypedEventEmitter} from "./util/TypedEventEmitter";
import {LinkUpEvents} from "./LinkUpEvents";
/**
* This class is the core component of the bot system. It handles connections to the Hyperswarm network, manages message sending and receiving, and emits events for various actions.
* @emits Client#onMessage
* @emits Client#onFile
* @emits Client#onAudio
* @emits Client#onIcon
*/
class Client extends EventEmitter {
export class Client extends TypedEventEmitter<LinkUpEvents> {
public botName: string = "";
public servePort: number | null = 0;
public storagePath: string | undefined;
public swarm: Hyperswarm | undefined;
public drive: Hyperdrive | undefined;
public store: Corestore | undefined;
public joinedRooms: Set<string> | undefined;
public currentTopic: string | null = null;
public botAvatar: string = "";
public iconMessage: IconMessage | undefined;
public discovery: PeerDiscovery | undefined;
/**
* @param {String} botName The name of the bot.
* @param botName The name of the bot.
* @since 1.0
* @constructor
* @author snxraven
*/
constructor(botName) {
constructor(botName: string) {
super();
if (!botName) return console.error("Bot Name is not defined!");
if (!botName) {
console.error("Bot Name is not defined!");
return;
}
this.botName = botName;
this.swarm = new Hyperswarm();
this.joinedRooms = new Set(); // Track the rooms the bot has joined
@ -75,6 +87,7 @@ class Client extends EventEmitter {
this.servePort = this.getRandomPort();
const serve = new ServeDrive({
port: this.servePort,
// @ts-ignore
get: ({ key, filename, version }) => this.drive
});
await serve.ready();
@ -87,23 +100,23 @@ class Client extends EventEmitter {
* @description Returns a random port number.
* @since 1.0
* @author snxraven
* @return {Number} Random port number.
* @return Random port number.
*/
getRandomPort() {
getRandomPort(): number {
return Math.floor(Math.random() * (65535 - 49152 + 1)) + 49152;
}
/**
* @description Fetches and sets the bot's avatar from a local file.
* @param {String} filePath path to the local avatar file.
* @param filePath path to the local avatar file.
* @since 1.0
* @author snxraven
*/
async fetchAvatar(filePath) {
async fetchAvatar(filePath: string) {
try {
await this.drive.ready();
await this.drive?.ready();
const iconBuffer = fs.readFileSync(filePath);
await this.drive.put(`/icons/${this.botName}.png`, iconBuffer);
await this.drive?.put(`/icons/${this.botName}.png`, iconBuffer);
this.botAvatar = `http://localhost:${this.servePort}/icons/${this.botName}.png`;
// Cache the icon message
@ -119,15 +132,15 @@ class Client extends EventEmitter {
* @author snxraven
*/
setupSwarm() {
this.swarm.on('connection', (peer) => {
this.swarm?.on('connection', (peer) => {
// Send the cached icon message to the new peer
if (this.iconMessage) {
peer.write(this.iconMessage.toJsonString());
}
peer.on('data', async message => {
peer.on('data', async (message: {}) => {
const messageObj = JSON.parse(message.toString());
if (this.joinedRooms.has(messageObj.topic)) { // Process message only if it is from a joined room
if (this.joinedRooms?.has(messageObj.topic)) { // Process message only if it is from a joined room
this.currentTopic = messageObj.topic; // Set the current topic from the incoming message
const msgType = messageObj.type;
@ -137,35 +150,23 @@ class Client extends EventEmitter {
if (msgType === "message")
/**
* Triggered when a new message is received.
*
* @event Client#onMessage
* @property peer - HyperSwarm peer object
* @property {TextMessage} textMessage -Class with all of the information about received text message
* @example
* const bot = new Client("MyBot");
* bot.on('onMessage', (peer, message) => {
* console.log(`Message from ${message.peerName}: ${message.message}`);
* });
*/
this.emit('onMessage', peer, new TextMessage(peerName, peerAvatar, this.currentTopic, timestamp, messageObj.message));
this.emit('onMessage', new TextMessage(peerName, peerAvatar, this.currentTopic, timestamp, messageObj.message));
if (msgType === "file") {
const fileBuffer = await this.drive.get(`/files/${messageObj.fileName}`);
const fileBuffer = await this.drive?.get(`/files/${messageObj.fileName}`);
/**
* Triggered when a file message is received.
*
* @event Client#onFile
* @property peer - HyperSwarm peer object
* @property {FileMessage} fileMessage - Class with all of the information about received file
* @property {FileMessage} FileMessage - Class with all of the information about received file
* @example
* const bot = new Client("MyBot");
* bot.on('onFile', (peer, message) => {
* console.log(`Received file from ${message.peerName}`);
* });
*/
this.emit('onFile', peer, new FileMessage(peerName, peerAvatar, this.currentTopic, timestamp, messageObj.fileName, `http://localhost:${this.servePort}/files/${messageObj.fileName}`, messageObj.fileType, messageObj.fileData));
this.emit('onFile', new FileMessage(peerName, peerAvatar, this.currentTopic, timestamp, messageObj.fileName, `http://localhost:${this.servePort}/files/${messageObj.fileName}`, messageObj.fileType, messageObj.fileData));
}
if (msgType === "icon")
@ -174,62 +175,65 @@ class Client extends EventEmitter {
*
* @event Client#onIcon
* @property peer - HyperSwarm peer object
* @property {IconMessage} iconMessage - Class with all of the information about received peer icon
* @property {IconMessage} IconMessage - Class with all of the information about received peer icon
* @example
* const bot = new Client("MyBot");
* bot.on('onIcon', (peer, message) => {
* console.log(`Received new Icon from ${message.peerName}`);
* });
*/
this.emit('onIcon', peer, new IconMessage(peerName, peerAvatar, timestamp));
this.emit('onIcon', new IconMessage(peerName, peerAvatar, timestamp));
if (msgType === "audio") {
const audioBuffer = await this.drive.get(`/audio/${messageObj.audioName}`);
const audioBuffer = await this.drive?.get(`/audio/${messageObj.audioName}`);
/**
* Triggered when an audio message is received.
*
* @event Client#onAudio
* @property peer - HyperSwarm peer object
* @property {AudioMessage} audioMessage - Class with all of the information about received audio file
* @property {AudioMessage} AudioMessage - Class with all of the information about received audio file
* @example
* ```js
* const bot = new Client("MyBot");
* bot.on('onAudio', (peer, message) => {
* console.log(`Received audio file from ${message.peerName}`);
* });
* ```
*/
this.emit('onAudio', peer, new AudioMessage(peerName, peerAvatar, this.currentTopic, timestamp, `http://localhost:${this.servePort}/audio/${messageObj.audioName}`, messageObj.audioType, messageObj.audioData));
this.emit('onAudio', new AudioMessage(peerName, peerAvatar, this.currentTopic, timestamp, `http://localhost:${this.servePort}/audio/${messageObj.audioName}`, messageObj.audioType, messageObj.audioData));
}
}
});
peer.on('error', e => {
this.emit('onError', e);
console.error(`Connection error: ${e}`);
peer.on('error', (err: any) => {
this.emit('onError', err);
console.error(`Connection error: ${err}`);
});
});
this.swarm.on('update', () => {
console.log(`Connections count: ${this.swarm.connections.size} / Peers count: ${this.swarm.peers.size}`);
// @ts-ignore
this.swarm.on("update", () => {
console.log(`Connections count: ${this.swarm?.connections.size} / Peers count: ${this.swarm?.peers.size}`);
});
}
/**
* @description Joins a specified chat room.
* @since 1.0
* @author snxraven
* @param {String} chatRoomID Chat room topic string
* @param chatRoomID Chat room topic string
*/
joinChatRoom(chatRoomID) {
if (!chatRoomID || typeof chatRoomID !== 'string') {
joinChatRoom(chatRoomID: string) {
if (!chatRoomID) {
console.error("Invalid chat room ID!");
return;
}
this.joinedRooms.add(chatRoomID); // Add the room to the list of joined rooms
this.joinedRooms?.add(chatRoomID); // Add the room to the list of joined rooms
this.currentTopic = chatRoomID; // Store the current topic
this.discovery = this.swarm.join(Buffer.from(chatRoomID, 'hex'), { client: true, server: true });
this.discovery.flushed().then(() => {
this.discovery = this.swarm?.join(Buffer.from(chatRoomID, 'hex'), { client: true, server: true });
this.discovery?.flushed().then(() => {
console.log(`Bot ${this.botName} joined the chat room.`);
this.emit('onBotJoinRoom');
this.emit('onBotJoinRoom', chatRoomID);
});
}
@ -237,9 +241,9 @@ class Client extends EventEmitter {
* @description Sends a text message.
* @since 1.0
* @author MiTask
* @param {String} message Text message to send to the bot's current chat room.
* @param message Text message to send to the bot's current chat room.
*/
sendTextMessage(message) {
sendTextMessage(message: string) {
console.log(`Preparing to send text message: ${message}`);
this.sendMessage(TextMessage.new(this, message));
}
@ -248,15 +252,15 @@ class Client extends EventEmitter {
* @description Sends a file message.
* @since 1.0
* @author snxraven
* @param {String} filePath Path to the file to send.
* @param {String} fileType Type of the file to send.
* @param filePath Path to the file to send.
* @param fileType Type of the file to send.
*/
async sendFileMessage(filePath, fileType) {
async sendFileMessage(filePath: string, fileType: string) {
try {
await this.drive.ready();
await this.drive?.ready();
const fileBuffer = fs.readFileSync(filePath);
const fileName = path.basename(filePath);
await this.drive.put(`/files/${fileName}`, fileBuffer);
await this.drive?.put(`/files/${fileName}`, fileBuffer);
const fileUrl = `http://localhost:${this.servePort}/files/${fileName}`;
const fileMessage = FileMessage.new(this, fileName, fileUrl, fileType, fileBuffer); // Pass fileBuffer to the new method
this.sendMessage(fileMessage);
@ -269,15 +273,15 @@ class Client extends EventEmitter {
* @description Sends an audio message.
* @since 1.0
* @author snxraven
* @param {String} filePath Path to the audio file to send.
* @param {String} audioType Type of the audio file to send.
* @param filePath Path to the audio file to send.
* @param audioType Type of the audio file to send.
*/
async sendAudioMessage(filePath, audioType) {
async sendAudioMessage(filePath: string, audioType: string) {
try {
await this.drive.ready();
await this.drive?.ready();
const audioBuffer = fs.readFileSync(filePath);
const audioName = path.basename(filePath);
await this.drive.put(`/audio/${audioName}`, audioBuffer);
await this.drive?.put(`/audio/${audioName}`, audioBuffer);
const audioUrl = `http://localhost:${this.servePort}/audio/${audioName}`;
const audioMessage = AudioMessage.new(this, audioUrl, audioType, audioBuffer); // Pass audioBuffer to the new method
this.sendMessage(audioMessage);
@ -291,17 +295,12 @@ class Client extends EventEmitter {
* @description Sends a generic message.
* @since 1.0
* @author MiTask
* @param {Message} message Message class (TextMessage, FileMessage or AudioMessage)
* @param message Message class (TextMessage, FileMessage or AudioMessage)
*/
sendMessage(message) {
if (!(message instanceof Message)) {
console.error(`message does not extend Message class (TextMessage, FileMessage, AudioMessage).`, message);
return;
}
sendMessage(message: Message) {
console.log("Sending message:", message);
const data = message.toJsonString();
const peers = [...this.swarm.connections];
const peers = [...this.swarm?.connections];
if (peers.length === 0) {
console.warn("No active peer connections found.");
return;
@ -324,9 +323,7 @@ class Client extends EventEmitter {
* @author snxraven
*/
async destroy() {
await this.swarm.destroy();
await this.swarm?.destroy();
console.log(`Bot ${this.botName} disconnected.`);
}
}
export default Client;
}

24
src/LinkUpEvents.ts Normal file
View File

@ -0,0 +1,24 @@
import {TextMessage} from "./message/TextMessage";
import {FileMessage} from "./message/FileMessage";
import {AudioMessage} from "./message/AudioMessage";
import {IconMessage} from "./message/IconMessage";
export type LinkUpEvents = {
/**
* Triggered when a new message is received.
*
* @event Client#onMessage
* @property {TextMessage} TextMessage - Class with all of the information about received text message
* @example
* const bot = new Client("MyBot");
* bot.on('onMessage', (message) => {
* console.log(`Message from ${message.peerName}: ${message.message}`);
* });
*/
'onMessage': [textMessage: TextMessage]
'onFile': [fileMessage: FileMessage]
'onAudio': [audioMessage: AudioMessage]
'onIcon': [iconMessage: IconMessage]
'onError': [error: any]
'onBotJoinRoom': [currentTopic: string]
}

View File

@ -1,21 +1,29 @@
import Message from "./Message.js";
import b4a from "b4a";
/**
* @module Message
*/
class AudioMessage extends Message {
import {Message} from "./Message";
import b4a from "b4a";
import {Client} from "../Client";
export class AudioMessage extends Message {
audioUrl: string;
audioType: string;
audioData: string;
/**
* @description Creates a new Audio message.
* @since 1.0
* @author MiTask
* @constructor
* @param {String} peerName Peer username
* @param {String} peerAvatar Peer avatar URL
* @param {String} topic Chat room topic string
* @param {Number} timestamp UNIX Timestamp
* @param {String} audioUrl URL to the audio file
* @param {String} audioType Type of the audio file
* @param {String} audioData Audio file data in base64 String format
* @param peerName Peer username
* @param peerAvatar Peer avatar URL
* @param topic Chat room topic string
* @param timestamp UNIX Timestamp
* @param audioUrl URL to the audio file
* @param audioType Type of the audio file
* @param audioData Audio file data in base64 String format
*/
constructor(peerName, peerAvatar, topic, timestamp, audioUrl, audioType, audioData) {
constructor(peerName: string, peerAvatar: string, topic: string | null, timestamp: number, audioUrl: string, audioType: string, audioData: string) {
super("audio", peerName, peerAvatar, topic, timestamp);
this.audioUrl = audioUrl;
this.audioType = audioType;
@ -27,7 +35,7 @@ class AudioMessage extends Message {
* @author MiTask
* @returns {String} JSON String with all of the information about the message
*/
toJsonString() {
toJsonString(): string {
return JSON.stringify({
...this.toJson(),
audioUrl: this.audioUrl,
@ -40,16 +48,14 @@ class AudioMessage extends Message {
* @description Creates a new audio message instance.
* @since 1.0
* @author MiTask
* @param {Client} bot Bot Client class
* @param {String} audioUrl URL to the audio file
* @param {String} audioType Type of the audio file
* @param {Buffer} audioBuffer Audio file data
* @param bot Bot Client class
* @param audioUrl URL to the audio file
* @param audioType Type of the audio file
* @param audioBuffer Audio file data
* @returns {AudioMessage} AudioMessage instance.
*/
static new(bot, audioUrl, audioType, audioBuffer) {
static new(bot: Client, audioUrl: string, audioType: string, audioBuffer: Buffer): AudioMessage {
const audioData = b4a.toString(audioBuffer, 'base64'); // Convert audio buffer to base64
return new AudioMessage(bot.botName, bot.botAvatar, bot.currentTopic, Date.now(), audioUrl, audioType, audioData);
}
}
export default AudioMessage;

View File

@ -1,22 +1,31 @@
import Message from "./Message.js";
import b4a from "b4a";
/**
* @module Message
*/
class FileMessage extends Message {
import {Message} from "./Message";
import b4a from "b4a";
import {Client} from "../Client";
export class FileMessage extends Message {
fileName: string;
fileUrl: string;
fileType: string;
fileData: string;
/**
* @description Creates a new file message.
* @since 1.0
* @author MiTask
* @constructor
* @param {String} peerName Peer username
* @param {String} peerAvatar Peer avatar URL
* @param {String} topic Chat room topic string
* @param {Number} timestamp UNIX Timestamp
* @param {String} fileName File name
* @param {String} fileUrl URL to the file
* @param {String} fileType Type of the file
* @param {String} fileData File data in base64 String format
* @param peerName Peer username
* @param peerAvatar Peer avatar URL
* @param topic Chat room topic string
* @param timestamp UNIX Timestamp
* @param fileName File name
* @param fileUrl URL to the file
* @param fileType Type of the file
* @param fileData File data in base64 String format
*/
constructor(peerName, peerAvatar, topic, timestamp, fileName, fileUrl, fileType, fileData) {
constructor(peerName: string, peerAvatar: string, topic: string | null, timestamp: number, fileName: string, fileUrl: string, fileType: string, fileData: string) {
super("file", peerName, peerAvatar, topic, timestamp);
this.fileName = fileName;
this.fileUrl = fileUrl;
@ -29,7 +38,7 @@ class FileMessage extends Message {
* @author MiTask
* @returns {String} JSON String with all of the information about the message
*/
toJsonString() {
toJsonString(): string {
return JSON.stringify({
...this.toJson(),
fileName: this.fileName,
@ -43,17 +52,15 @@ class FileMessage extends Message {
* @description Creates a new file message instance.
* @since 1.0
* @author MiTask
* @param {Client} bot Bot Client class
* @param {String} fileName File name
* @param {String} fileUrl URL to the file
* @param {String} fileType Type of the file
* @param {Buffer} fileBuffer File data
* @param bot Bot Client class
* @param fileName File name
* @param fileUrl URL to the file
* @param fileType Type of the file
* @param fileBuffer File data
* @returns {FileMessage} FileMessage instance.
*/
static new(bot, fileName, fileUrl, fileType, fileBuffer) {
static new(bot: Client, fileName: string, fileUrl: string, fileType: string, fileBuffer: Buffer): FileMessage {
const fileData = b4a.toString(fileBuffer, 'base64'); // Convert file buffer to base64
return new FileMessage(bot.botName, bot.botAvatar, bot.currentTopic, Date.now(), fileName, fileUrl, fileType, fileData);
}
}
export default FileMessage;
}

View File

@ -1,31 +0,0 @@
import Message from "./Message.js";
import b4a from "b4a";
class IconMessage extends Message {
/**
* @description Creates a new icon message.
* @since 1.0
* @author MiTask
* @constructor
* @param {String} peerName Peer username
* @param {String} peerAvatar Peer avatar URL
* @param {Number} timestamp UNIX Timestamp
*/
constructor(peerName, peerAvatar, timestamp) {
super("icon", peerName, peerAvatar, null, timestamp);
}
/**
* @description Creates a new icon message instance.
* @since 1.0
* @author MiTask
* @param {Client} bot Bot Client class
* @param {String} avatarBuffer Bot Avatar buffer
* @returns {IconMessage} IconMessage instance
*/
static new(bot, avatarBuffer) {
return new IconMessage(bot.botName, b4a.toString(avatarBuffer, 'base64'), Date.now());
}
}
export default IconMessage;

View File

@ -0,0 +1,34 @@
/**
* @module Message
*/
import {Message} from "./Message";
import b4a from "b4a";
import {Client} from "../Client";
export class IconMessage extends Message {
/**
* @description Creates a new icon message.
* @since 1.0
* @author MiTask
* @constructor
* @param peerName Peer username
* @param peerAvatar Peer avatar URL
* @param timestamp UNIX Timestamp
*/
constructor(peerName: string, peerAvatar: string, timestamp: number) {
super("icon", peerName, peerAvatar, null, timestamp);
}
/**
* @description Creates a new icon message instance.
* @since 1.0
* @author MiTask
* @param bot Bot Client class
* @param avatarBuffer Bot Avatar buffer
* @returns IconMessage instance
*/
static new(bot: Client, avatarBuffer: Buffer): IconMessage {
return new IconMessage(bot.botName, b4a.toString(avatarBuffer, 'base64'), Date.now());
}
}

View File

@ -1,52 +0,0 @@
/**
* @description Base class for all messages
* @since 1.0
* @author MiTask
*/
class Message {
/**
* @since 1.0
* @author MiTask
* @constructor
* @param {String} messageType Type of the message (text, file, audio, icon)
* @param {String} peerName Peer username
* @param {String} peerAvatar Peer avatar URL
* @param {String} topic Chat room topic string
* @param {Number} timestamp UNIX Timestamp
*/
constructor(messageType, peerName, peerAvatar, topic, timestamp) {
this.type = messageType;
this.peerName = peerName;
this.peerAvatar = peerAvatar;
this.topic = topic;
this.timestamp = timestamp;
}
/**
* @since 1.0
* @author MiTask
* @returns {{name: String, topic: String, avatar: String, type: String, timestamp: Number}} JSON Object with all of the information about the message
*/
toJson() {
return {
type: this.type,
name: this.peerName,
avatar: this.peerAvatar,
topic: this.topic,
timestamp: this.timestamp
};
}
/**
* @since 1.0
* @author MiTask
* @returns {String} JSON String with all of the information about the message
*/
toJsonString() {
return JSON.stringify({
...this.toJson()
});
}
}
export default Message;

57
src/message/Message.ts Normal file
View File

@ -0,0 +1,57 @@
/**
* @description Base class for all messages
* @since 1.0
* @author MiTask
* @module Message
*/
export class Message {
type: string;
peerName: string;
peerAvatar: string;
topic: string | null;
timestamp: number;
/**
* @since 1.0
* @author MiTask
* @constructor
* @param messageType Type of the message (text, file, audio, icon)
* @param peerName Peer username
* @param peerAvatar Peer avatar URL
* @param topic Chat room topic string
* @param timestamp UNIX Timestamp
*/
constructor(messageType: string, peerName: string, peerAvatar: string, topic: string | null, timestamp: number) {
this.type = messageType;
this.peerName = peerName;
this.peerAvatar = peerAvatar;
this.topic = topic;
this.timestamp = timestamp;
}
/**
* @since 1.0
* @author MiTask
* @returns JSON Object with all of the information about the message
*/
protected toJson(): {name: String, topic: String, avatar: String, type: String, timestamp: Number} {
return {
type: this.type,
name: this.peerName,
avatar: this.peerAvatar,
topic: this.topic ? this.topic : "",
timestamp: this.timestamp
};
}
/**
* @since 1.0
* @author MiTask
* @returns {string} JSON String with all of the information about the message
*/
toJsonString(): string {
return JSON.stringify({
...this.toJson()
});
}
}

View File

@ -1,18 +1,24 @@
import Message from "./Message.js";
/**
* @module Message
*/
class TextMessage extends Message {
import {Message} from "./Message";
import {Client} from "../Client";
export class TextMessage extends Message {
message: string;
/**
* @description Creates a new text message.
* @since 1.0
* @author MiTask
* @constructor
* @param {String} peerName Peer username
* @param {String} peerAvatar Peer avatar URL
* @param {String} topic Chat room topic string
* @param {Number} timestamp UNIX Timestamp
* @param {String} message Text of the message
* @param peerName Peer username
* @param peerAvatar Peer avatar URL
* @param topic Chat room topic string
* @param timestamp UNIX Timestamp
* @param message Text of the message
*/
constructor(peerName, peerAvatar, topic, timestamp, message) {
constructor(peerName: string, peerAvatar: string, topic: string | null, timestamp: number, message: string) {
super("message", peerName, peerAvatar, topic, timestamp);
this.message = message;
}
@ -22,7 +28,7 @@ class TextMessage extends Message {
* @author MiTask
* @returns {String} JSON String with all of the information about the message
*/
toJsonString() {
toJsonString(): string {
return JSON.stringify({
...this.toJson(),
message: this.message,
@ -33,13 +39,11 @@ class TextMessage extends Message {
* @description Creates a new text message instance.
* @since 1.0
* @author MiTask
* @param {Client} bot Bot Client class
* @param {String} message Text of the message
* @param bot Bot Client class
* @param message Text of the message
* @returns {TextMessage} TextMessage instance
*/
static new(bot, message) {
static new(bot: Client, message: string): TextMessage {
return new TextMessage(bot.botName, bot.botAvatar, bot.currentTopic, Date.now(), message);
}
}
export default TextMessage;
}

View File

@ -0,0 +1,39 @@
import {EventEmitter} from "stream";
/**
* This class is used for TypeSafe events.
* @internal
*/
export class TypedEventEmitter<TEvents extends Record<string, any>> {
private emitter = new EventEmitter()
/**
* @internal
*/
emit<TEventName extends keyof TEvents & string>(
eventName: TEventName,
...eventArg: TEvents[TEventName]
) {
this.emitter.emit(eventName, ...(eventArg as []))
}
/**
* @internal
*/
on<TEventName extends keyof TEvents & string>(
eventName: TEventName,
handler: (...eventArg: TEvents[TEventName]) => void
) {
this.emitter.on(eventName, handler as any)
}
/**
* @internal
*/
off<TEventName extends keyof TEvents & string>(
eventName: TEventName,
handler: (...eventArg: TEvents[TEventName]) => void
) {
this.emitter.off(eventName, handler as any)
}
}

19
typedoc.json Normal file
View File

@ -0,0 +1,19 @@
{
"out": "./docs",
"includes": "./src",
"entryPoints": ["./src/**/*"],
"exclude": ["./src/util/*"],
"entryPointStrategy": "expand",
"cleanOutputDir": true,
"jsDocCompatibility": true,
"tsconfig": "tsconfig.json",
"githubPages": false,
"plugin": ["typedoc-plugin-merge-modules"],
"excludeReferences": true,
"mergeModulesRenameDefaults": true,
"mergeModulesMergeMode": "module-category",
"emit": "both",
"disableSources": true,
"excludeExternals": true,
"excludeInternal": true
}