Compare commits
No commits in common. "623645090d8a8317e087296fed1acba61615e165" and "f0a1f53c24ea327392a515c12d9d00401b81d4e7" have entirely different histories.
623645090d
...
f0a1f53c24
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,5 +1,4 @@
|
||||
.idea/
|
||||
package-lock.json
|
||||
node_modules
|
||||
docs
|
||||
storage
|
||||
docs
|
51
jsdoc.json
Normal file
51
jsdoc.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
package.json
20
package.json
@ -1,16 +1,13 @@
|
||||
{
|
||||
"name": "linkup-bot-lib",
|
||||
"version": "1.1.0",
|
||||
"main": "lib/Client.js",
|
||||
"types": "lib/*.ts",
|
||||
"version": "1.0.0",
|
||||
"main": "src/Client.js",
|
||||
"scripts": {
|
||||
"prepublishOnly": "typedoc",
|
||||
"build": "typedoc"
|
||||
"generate-docs": "jsdoc --configure jsdoc.json"
|
||||
},
|
||||
"keywords": [
|
||||
"linkup"
|
||||
],
|
||||
"author": "LinkUp Team",
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"type": "module",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
@ -21,8 +18,7 @@
|
||||
"serve-drive": "^5.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/b4a": "^1.6.4",
|
||||
"@types/node": "^20.14.2",
|
||||
"typedoc-plugin-merge-modules": "^5.1.0"
|
||||
"docdash": "^2.0.2",
|
||||
"jsdoc": "^4.0.3"
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +1,34 @@
|
||||
import path from 'path';
|
||||
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 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 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
|
||||
*/
|
||||
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;
|
||||
|
||||
class Client extends EventEmitter {
|
||||
/**
|
||||
* @param botName The name of the bot.
|
||||
* @param {String} botName The name of the bot.
|
||||
* @since 1.0
|
||||
* @constructor
|
||||
* @author snxraven
|
||||
*/
|
||||
constructor(botName: string) {
|
||||
constructor(botName) {
|
||||
super();
|
||||
if (!botName) {
|
||||
console.error("Bot Name is not defined!");
|
||||
return;
|
||||
}
|
||||
if (!botName) return console.error("Bot Name is not defined!");
|
||||
this.botName = botName;
|
||||
this.swarm = new Hyperswarm();
|
||||
this.joinedRooms = new Set(); // Track the rooms the bot has joined
|
||||
@ -87,7 +75,6 @@ export class Client extends TypedEventEmitter<LinkUpEvents> {
|
||||
this.servePort = this.getRandomPort();
|
||||
const serve = new ServeDrive({
|
||||
port: this.servePort,
|
||||
// @ts-ignore
|
||||
get: ({ key, filename, version }) => this.drive
|
||||
});
|
||||
await serve.ready();
|
||||
@ -100,23 +87,23 @@ export class Client extends TypedEventEmitter<LinkUpEvents> {
|
||||
* @description Returns a random port number.
|
||||
* @since 1.0
|
||||
* @author snxraven
|
||||
* @return Random port number.
|
||||
* @return {Number} Random port number.
|
||||
*/
|
||||
getRandomPort(): number {
|
||||
getRandomPort() {
|
||||
return Math.floor(Math.random() * (65535 - 49152 + 1)) + 49152;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Fetches and sets the bot's avatar from a local file.
|
||||
* @param filePath path to the local avatar file.
|
||||
* @param {String} filePath path to the local avatar file.
|
||||
* @since 1.0
|
||||
* @author snxraven
|
||||
*/
|
||||
async fetchAvatar(filePath: string) {
|
||||
async fetchAvatar(filePath) {
|
||||
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
|
||||
@ -132,15 +119,15 @@ export class Client extends TypedEventEmitter<LinkUpEvents> {
|
||||
* @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;
|
||||
@ -150,23 +137,35 @@ export class Client extends TypedEventEmitter<LinkUpEvents> {
|
||||
|
||||
|
||||
if (msgType === "message")
|
||||
this.emit('onMessage', new TextMessage(peerName, peerAvatar, this.currentTopic, timestamp, messageObj.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));
|
||||
|
||||
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', new FileMessage(peerName, peerAvatar, this.currentTopic, timestamp, messageObj.fileName, `http://localhost:${this.servePort}/files/${messageObj.fileName}`, messageObj.fileType, messageObj.fileData));
|
||||
this.emit('onFile', peer, new FileMessage(peerName, peerAvatar, this.currentTopic, timestamp, messageObj.fileName, `http://localhost:${this.servePort}/files/${messageObj.fileName}`, messageObj.fileType, messageObj.fileData));
|
||||
}
|
||||
|
||||
if (msgType === "icon")
|
||||
@ -175,65 +174,62 @@ export class Client extends TypedEventEmitter<LinkUpEvents> {
|
||||
*
|
||||
* @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', new IconMessage(peerName, peerAvatar, timestamp));
|
||||
this.emit('onIcon', peer, 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', new AudioMessage(peerName, peerAvatar, this.currentTopic, timestamp, `http://localhost:${this.servePort}/audio/${messageObj.audioName}`, messageObj.audioType, messageObj.audioData));
|
||||
this.emit('onAudio', peer, new AudioMessage(peerName, peerAvatar, this.currentTopic, timestamp, `http://localhost:${this.servePort}/audio/${messageObj.audioName}`, messageObj.audioType, messageObj.audioData));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
peer.on('error', (err: any) => {
|
||||
this.emit('onError', err);
|
||||
console.error(`Connection error: ${err}`);
|
||||
peer.on('error', e => {
|
||||
this.emit('onError', e);
|
||||
console.error(`Connection error: ${e}`);
|
||||
});
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
this.swarm.on("update", () => {
|
||||
console.log(`Connections count: ${this.swarm?.connections.size} / Peers count: ${this.swarm?.peers.size}`);
|
||||
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 chatRoomID Chat room topic string
|
||||
* @param {String} chatRoomID Chat room topic string
|
||||
*/
|
||||
joinChatRoom(chatRoomID: string) {
|
||||
if (!chatRoomID) {
|
||||
joinChatRoom(chatRoomID) {
|
||||
if (!chatRoomID || typeof chatRoomID !== 'string') {
|
||||
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', chatRoomID);
|
||||
this.emit('onBotJoinRoom');
|
||||
});
|
||||
}
|
||||
|
||||
@ -241,9 +237,9 @@ export class Client extends TypedEventEmitter<LinkUpEvents> {
|
||||
* @description Sends a text message.
|
||||
* @since 1.0
|
||||
* @author MiTask
|
||||
* @param message Text message to send to the bot's current chat room.
|
||||
* @param {String} message Text message to send to the bot's current chat room.
|
||||
*/
|
||||
sendTextMessage(message: string) {
|
||||
sendTextMessage(message) {
|
||||
console.log(`Preparing to send text message: ${message}`);
|
||||
this.sendMessage(TextMessage.new(this, message));
|
||||
}
|
||||
@ -252,15 +248,15 @@ export class Client extends TypedEventEmitter<LinkUpEvents> {
|
||||
* @description Sends a file message.
|
||||
* @since 1.0
|
||||
* @author snxraven
|
||||
* @param filePath Path to the file to send.
|
||||
* @param fileType Type of the file to send.
|
||||
* @param {String} filePath Path to the file to send.
|
||||
* @param {String} fileType Type of the file to send.
|
||||
*/
|
||||
async sendFileMessage(filePath: string, fileType: string) {
|
||||
async sendFileMessage(filePath, fileType) {
|
||||
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);
|
||||
@ -273,15 +269,15 @@ export class Client extends TypedEventEmitter<LinkUpEvents> {
|
||||
* @description Sends an audio message.
|
||||
* @since 1.0
|
||||
* @author snxraven
|
||||
* @param filePath Path to the audio file to send.
|
||||
* @param audioType Type of the audio file to send.
|
||||
* @param {String} filePath Path to the audio file to send.
|
||||
* @param {String} audioType Type of the audio file to send.
|
||||
*/
|
||||
async sendAudioMessage(filePath: string, audioType: string) {
|
||||
async sendAudioMessage(filePath, audioType) {
|
||||
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);
|
||||
@ -295,12 +291,17 @@ export class Client extends TypedEventEmitter<LinkUpEvents> {
|
||||
* @description Sends a generic message.
|
||||
* @since 1.0
|
||||
* @author MiTask
|
||||
* @param message Message class (TextMessage, FileMessage or AudioMessage)
|
||||
* @param {Message} message Message class (TextMessage, FileMessage or AudioMessage)
|
||||
*/
|
||||
sendMessage(message: Message) {
|
||||
sendMessage(message) {
|
||||
if (!(message instanceof Message)) {
|
||||
console.error(`message does not extend Message class (TextMessage, FileMessage, AudioMessage).`, message);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
@ -323,7 +324,9 @@ export class Client extends TypedEventEmitter<LinkUpEvents> {
|
||||
* @author snxraven
|
||||
*/
|
||||
async destroy() {
|
||||
await this.swarm?.destroy();
|
||||
await this.swarm.destroy();
|
||||
console.log(`Bot ${this.botName} disconnected.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Client;
|
@ -1,24 +0,0 @@
|
||||
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]
|
||||
}
|
@ -1,29 +1,21 @@
|
||||
/**
|
||||
* @module Message
|
||||
*/
|
||||
|
||||
import {Message} from "./Message";
|
||||
import Message from "./Message.js";
|
||||
import b4a from "b4a";
|
||||
import {Client} from "../Client";
|
||||
|
||||
export class AudioMessage extends Message {
|
||||
audioUrl: string;
|
||||
audioType: string;
|
||||
audioData: string;
|
||||
class AudioMessage extends Message {
|
||||
/**
|
||||
* @description Creates a new Audio message.
|
||||
* @since 1.0
|
||||
* @author MiTask
|
||||
* @constructor
|
||||
* @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
|
||||
* @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
|
||||
*/
|
||||
constructor(peerName: string, peerAvatar: string, topic: string | null, timestamp: number, audioUrl: string, audioType: string, audioData: string) {
|
||||
constructor(peerName, peerAvatar, topic, timestamp, audioUrl, audioType, audioData) {
|
||||
super("audio", peerName, peerAvatar, topic, timestamp);
|
||||
this.audioUrl = audioUrl;
|
||||
this.audioType = audioType;
|
||||
@ -35,7 +27,7 @@ export class AudioMessage extends Message {
|
||||
* @author MiTask
|
||||
* @returns {String} JSON String with all of the information about the message
|
||||
*/
|
||||
toJsonString(): string {
|
||||
toJsonString() {
|
||||
return JSON.stringify({
|
||||
...this.toJson(),
|
||||
audioUrl: this.audioUrl,
|
||||
@ -48,14 +40,16 @@ export class AudioMessage extends Message {
|
||||
* @description Creates a new audio message instance.
|
||||
* @since 1.0
|
||||
* @author MiTask
|
||||
* @param bot Bot Client class
|
||||
* @param audioUrl URL to the audio file
|
||||
* @param audioType Type of the audio file
|
||||
* @param audioBuffer Audio file data
|
||||
* @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
|
||||
* @returns {AudioMessage} AudioMessage instance.
|
||||
*/
|
||||
static new(bot: Client, audioUrl: string, audioType: string, audioBuffer: Buffer): AudioMessage {
|
||||
static new(bot, audioUrl, audioType, audioBuffer) {
|
||||
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;
|
@ -1,31 +1,22 @@
|
||||
/**
|
||||
* @module Message
|
||||
*/
|
||||
|
||||
import {Message} from "./Message";
|
||||
import Message from "./Message.js";
|
||||
import b4a from "b4a";
|
||||
import {Client} from "../Client";
|
||||
|
||||
export class FileMessage extends Message {
|
||||
fileName: string;
|
||||
fileUrl: string;
|
||||
fileType: string;
|
||||
fileData: string;
|
||||
class FileMessage extends Message {
|
||||
/**
|
||||
* @description Creates a new file message.
|
||||
* @since 1.0
|
||||
* @author MiTask
|
||||
* @constructor
|
||||
* @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
|
||||
* @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
|
||||
*/
|
||||
constructor(peerName: string, peerAvatar: string, topic: string | null, timestamp: number, fileName: string, fileUrl: string, fileType: string, fileData: string) {
|
||||
constructor(peerName, peerAvatar, topic, timestamp, fileName, fileUrl, fileType, fileData) {
|
||||
super("file", peerName, peerAvatar, topic, timestamp);
|
||||
this.fileName = fileName;
|
||||
this.fileUrl = fileUrl;
|
||||
@ -38,7 +29,7 @@ export class FileMessage extends Message {
|
||||
* @author MiTask
|
||||
* @returns {String} JSON String with all of the information about the message
|
||||
*/
|
||||
toJsonString(): string {
|
||||
toJsonString() {
|
||||
return JSON.stringify({
|
||||
...this.toJson(),
|
||||
fileName: this.fileName,
|
||||
@ -52,15 +43,17 @@ export class FileMessage extends Message {
|
||||
* @description Creates a new file message instance.
|
||||
* @since 1.0
|
||||
* @author MiTask
|
||||
* @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
|
||||
* @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
|
||||
* @returns {FileMessage} FileMessage instance.
|
||||
*/
|
||||
static new(bot: Client, fileName: string, fileUrl: string, fileType: string, fileBuffer: Buffer): FileMessage {
|
||||
static new(bot, fileName, fileUrl, fileType, fileBuffer) {
|
||||
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;
|
31
src/message/IconMessage.js
Normal file
31
src/message/IconMessage.js
Normal file
@ -0,0 +1,31 @@
|
||||
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;
|
@ -1,34 +0,0 @@
|
||||
/**
|
||||
* @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());
|
||||
}
|
||||
}
|
52
src/message/Message.js
Normal file
52
src/message/Message.js
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @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;
|
@ -1,57 +0,0 @@
|
||||
/**
|
||||
* @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()
|
||||
});
|
||||
}
|
||||
}
|
@ -1,24 +1,18 @@
|
||||
/**
|
||||
* @module Message
|
||||
*/
|
||||
import Message from "./Message.js";
|
||||
|
||||
import {Message} from "./Message";
|
||||
import {Client} from "../Client";
|
||||
|
||||
export class TextMessage extends Message {
|
||||
message: string;
|
||||
class TextMessage extends Message {
|
||||
/**
|
||||
* @description Creates a new text message.
|
||||
* @since 1.0
|
||||
* @author MiTask
|
||||
* @constructor
|
||||
* @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
|
||||
* @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
|
||||
*/
|
||||
constructor(peerName: string, peerAvatar: string, topic: string | null, timestamp: number, message: string) {
|
||||
constructor(peerName, peerAvatar, topic, timestamp, message) {
|
||||
super("message", peerName, peerAvatar, topic, timestamp);
|
||||
this.message = message;
|
||||
}
|
||||
@ -28,7 +22,7 @@ export class TextMessage extends Message {
|
||||
* @author MiTask
|
||||
* @returns {String} JSON String with all of the information about the message
|
||||
*/
|
||||
toJsonString(): string {
|
||||
toJsonString() {
|
||||
return JSON.stringify({
|
||||
...this.toJson(),
|
||||
message: this.message,
|
||||
@ -39,11 +33,13 @@ export class TextMessage extends Message {
|
||||
* @description Creates a new text message instance.
|
||||
* @since 1.0
|
||||
* @author MiTask
|
||||
* @param bot Bot Client class
|
||||
* @param message Text of the message
|
||||
* @param {Client} bot Bot Client class
|
||||
* @param {String} message Text of the message
|
||||
* @returns {TextMessage} TextMessage instance
|
||||
*/
|
||||
static new(bot: Client, message: string): TextMessage {
|
||||
static new(bot, message) {
|
||||
return new TextMessage(bot.botName, bot.botAvatar, bot.currentTopic, Date.now(), message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default TextMessage;
|
@ -1,39 +0,0 @@
|
||||
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
19
typedoc.json
@ -1,19 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
Loading…
Reference in New Issue
Block a user