diff --git a/src/chat-messages.js b/src/chat-messages.js index e7c4caa..573966f 100644 --- a/src/chat-messages.js +++ b/src/chat-messages.js @@ -16,18 +16,20 @@ const ChatMessages = function (privateClient, publicClient) { /** * Schema: chat-messages/daily * - * Represents one day of chat messages + * Represents one calendar day of chat messages * - * Example: - * - * (start code) + * @example * { * "@context": "https://kosmos.org/ns/v1", - * "@id": "chat-messages/freenode/channels/kosmos/", + * "@id": "chat-messages/irc.libera.chat/channels/kosmos/", * "@type": "ChatChannel", + * "service": { + * "domain": "irc.libera.chat", + * "protocol": "IRC", + * }, * "name": "#kosmos", - * "ircURI": "irc://irc.freenode.net/kosmos", - * "today": { + * "type": "room", + * "today": { * "@id": "2015/01/01", * "@type": "ChatLog", * "messageType": "InstantMessage", @@ -40,9 +42,7 @@ const ChatMessages = function (privateClient, publicClient) { * ] * } * } - * (end code) */ - const archiveSchema = { "type": "object", "properties": { @@ -60,17 +60,27 @@ const ChatMessages = function (privateClient, publicClient) { "default": "ChatChannel", "enum": ["ChatChannel"] }, + "service": { + "type": "object", + "properties": { + "domain": { + "type": "string", + "required": true + }, + "protocol": { + "type": "string", + "required": true + } + } + }, "name": { "type": "string", "required": true }, - "ircURI": { + "type": { "type": "string", - "format": "uri" - }, - "xmppURI": { - "type": "string", - "format": "uri" + "required": true, + "enum": [ "room", "person" ] }, "today": { "type": "object", @@ -135,56 +145,55 @@ const ChatMessages = function (privateClient, publicClient) { publicClient.declareType("daily-archive", "https://kosmos.org/ns/v1", archiveSchema); /** - * Class: DailyArchive + * A daily archive stores chat messages by calendar day. * - * A daily archive stores IRC messages by day. - * - * Parameters (object): - * server - Chat server info (see ) - * channelName - Name of room/channel - * date - Date of archive day - * isPublic - Store logs in public folder (defaults to false) - * previous - Date of previous log file as YYYY/MM/DD; - * looked up automatically when not given - * next - Date of next log file as YYYY/MM/DD; - * looked up automatically when not given - * - * Example for IRC: - * - * (start code) - * const archive = new chatMessages.DailyArchive({ - * server: { - * type: 'irc', - * name: 'freenode', - * ircURI: 'irc://irc.freenode.net' - * }, - * channelName: '#kosmos', - * date: new Date(), - * isPublic: true - * }); - * (end code) - * - * Example for XMPP: - * - * (start code) - * const archive = new chatMessages.DailyArchive({ - * server: { - * type: 'xmpp', - * name: '5apps', - * xmppMUC: 'muc.5apps.com' - * }, - * channelName: 'watercooler', - * date: new Date(), - * isPublic: false - * }); - * (end code) + * @class */ class DailyArchive { + /** + * @param {object} options + * @param {object} options.service + * @param {string} options.service.protocol - Type of chat service/protocol (e.g. "IRC", "XMPP", "Campfire", "Slack") + * @param {string} options.service.domain - Domain of the chat service (e.g. "irc.libera.chat", "kosmos.chat") + * @param {string} options.channelName - Name of room/channel (e.g. "#kosmos") + * @param {string} [options.channelType] - Type of channel ("room" or "person") + * @param {date} options.date - Date of archive day + * @param {boolean} options.isPublic - Store logs in public folder (defaults to false) + * @param {string} [options.previous] - Date of previous log file as `YYYY/MM/DD`. Looked up automatically when not given + * @param {string} [options.next] - Date of next log file as `YYYY/MM/DD`. looked up automatically when not given + * + * @example + * // IRC archive: + * const archive = new chatMessages.DailyArchive({ + * service: { + * protocol: 'IRC', + * domain: 'irc.libera.chat', + * }, + * channelName: '#kosmos-dev', + * channelType: 'room', + * date: new Date(), + * isPublic: true + * }); + * + * // XMPP archive: + * const archive = new chatMessages.DailyArchive({ + * service: { + * protocol: 'XMPP', + * domain: 'kosmos.chat', + * }, + * channelName: 'kosmos-dev', + * channelType: 'room', + * date: new Date(), + * isPublic: false + * }); + * + */ constructor (options) { // // Defaults // - options.isPublic = options.isPublic || false; + options.isPublic = options.isPublic || false; + options.channelType = options.channelType || "room"; // // Validate options @@ -192,10 +201,10 @@ const ChatMessages = function (privateClient, publicClient) { if (typeof options !== "object") { throw "options must be an object"; } - if (typeof options.server !== "object" || - typeof options.server.type !== "string" || - typeof options.server.name !== "string") { - throw "server must be an object containing at least server \"type\" and \"name\""; + if (typeof options.service !== "object" || + typeof options.service.protocol !== "string" || + typeof options.service.domain !== "string") { + throw "service must be an object containing at least service \"protocol\" and \"domain\""; } if (typeof options.channelName !== "string") { throw "channelName must be a string"; @@ -208,104 +217,82 @@ const ChatMessages = function (privateClient, publicClient) { } /** - * Property: server - * - * Contains information about the chat server/network - * - * Properties: - * type - Type of server/protocol (e.g. "irc", "xmpp", "campfire", "slack") - * name - Shortname/id/alias of network/server (e.g. "freenode", "mycompanyname") - * ircURI - (optional) IRC URI of network (e.g. "irc://irc.freenode.net/") - * xmppMUC - (optional) XMPP MUC service host (e.g. "conference.jabber.org") + * @property {object} service + * @property {string} service.protocol - Type of chat service/protocol (e.g. "IRC", "XMPP", "campfire", "slack") + * @property {string} service.domain - Domain of the chat service (e.g. "irc.libera.chat", "kosmos.chat") */ - this.server = options.server; + this.service = options.service; /** - * Property: channelName - * - * Name of the IRC channel (e.g. "#kosmos") + * @property {string} channelName - Name of channel (e.g. "#kosmos") */ this.channelName = options.channelName; /** - * Property: date - * - * Date of the archive's content + * @property {string} channelType - Type of channel ("room" or "person") + */ + this.channelType = options.channelType; + + /** + * @property {string} date - Gregorian calendar date of the archive's content */ this.date = options.date; /** - * Property: isPublic - * - * `true` for public archives, `false` for private ones + * @property {boolean} isPublic - `true` for public archives, `false` for private ones */ - this.isPublic = options.isPublic; + this.isPublic = options.isPublic || false; /** - * Property: parsedDate - * - * Object containing padded year, month and day of date + * @property {object} parsedDate - Contains padded year, month and day of date + * @property {string} year + * @property {string} month + * @property {string} day */ this.parsedDate = parseDate(this.date); /** - * Property: dateId - * - * Date string in the form of YYYY/MM/DD + * @property {string} dateId - Date string in the form of YYYY/MM/DD */ this.dateId = this.parsedDate.year+'/'+this.parsedDate.month+'/'+this.parsedDate.day; /** - * Property: path - * - * Document path of the archive file + * @property {string} path - Document path of the archive file */ - switch (this.server.type) { - case 'irc': - if (this.channelName.match(/^#/)) { - // normal chatroom - const channelName = this.channelName.replace(/^#/,''); - this.path = `${this.server.name}/channels/${channelName}/${this.dateId}`; - } else { - // user direct message - this.path = `${this.server.name}/users/${this.channelName}/${this.dateId}`; - } - break; - default: - this.path = `${this.server.name}/${this.channelName}/${this.dateId}`; - break; + if (this.channelType === "room") { + // Normal chatroom + const channelName = this.channelName.replace(/#/,''); + this.path = `${this.service.domain}/channels/${channelName}/${this.dateId}`; + } else { + // User direct message + this.path = `${this.service.domain}/users/${this.channelName}/${this.dateId}`; } /** - * Property: client - * - * Public or private BaseClient, depending on isPublic + * @property {object} client - Public or private remoteStorgage.js BaseClient */ this.client = this.isPublic ? publicClient : privateClient; /** - * Property: previous - * - * Date of previous log file as YYYY/MM/DD + * @property {string} previous - Date of previous log file as YYYY/MM/DD */ this.previous = options.previous; /** - * Property: next - * - * Date of next log file as YYYY/MM/DD + * @property {string} next - Date of next log file as YYYY/MM/DD */ this.next = options.next; } /* - * Method: addMessage + * @param {object} message + * @param {string} message.timestamp - Timestamp of the message + * @param {string} message.from - The sender of the message + * @param {string} message.text - The message itself + * @param {string} message.type - Type of message (one of text, join, leave, action) + * @param {string} [message.id] - Unique ID of message. TODO implement * - * Parameters (object): - * timestamp - Timestamp of the message - * from - The sender of the message - * text - The message itself - * type - Type of message (one of text, join, leave, action) + * @returns {Promise} */ addMessage (message) { if (this.isPublic && !this.channelName.match(/^#/)) { @@ -324,15 +311,13 @@ const ChatMessages = function (privateClient, publicClient) { } /* - * Method: addMessages - * * Like , but for multiple messages at once. Useful for bulk * imports of messages. * - * Parameters: - * messages - Array of message objects (see params for addMessage) - * overwrite - If true, creates a new archive file and overwrites the - * old one. Defaults to false. + * @param {Array} messages - Array of message objects (see params for addMessage) + * @param {boolean} overwrite - If true, creates a new archive file and overwrites the old one. Defaults to false. + * + * @returns {Promise} */ addMessages (messages, overwrite) { if (this.isPublic && !this.channelName.match(/^#/)) { @@ -359,18 +344,20 @@ const ChatMessages = function (privateClient, publicClient) { } /* - * Method: remove - * * Deletes the entire archive document from storage + * + * @returns {Promise} */ remove () { return this.client.remove(this.path); } /* - * Method: _updateDocument - * * Updates and writes an existing archive document + * + * @returns {Promise} + * + * @private */ _updateDocument (archive, messages) { console.debug('[chat-messages] Updating archive document', archive); @@ -387,9 +374,11 @@ const ChatMessages = function (privateClient, publicClient) { } /* - * Method: _createDocument - * * Creates and writes a new archive document + * + * @returns {Promise} + * + * @private */ _createDocument (messages) { console.debug('[chat-messages] Creating new archive document'); @@ -421,17 +410,21 @@ const ChatMessages = function (privateClient, publicClient) { } /* - * Method: _buildArchiveObject - * * Builds the object to be stored in remote storage + * + * @returns {object} + * + * @private */ _buildArchiveObject () { const roomName = this.channelName.replace(/#/,''); const archive = { - "@id": "chat-messages/"+this.server.name+"/channels/"+roomName+"/", + "@id": "chat-messages/"+this.service.domain+"/channels/"+roomName+"/", "@type": "ChatChannel", + "service": this.service, "name": this.channelName, + "type": this.channelType, "today": { "@id": this.dateId, "@type": "ChatLog", @@ -440,15 +433,14 @@ const ChatMessages = function (privateClient, publicClient) { } }; - switch (this.server.type) { - case 'irc': + switch (this.service.protocol) { + case 'IRC': if (!this.channelName.match(/^#/)) { - archive["@id"] = "chat-messages/"+this.server.name+"/users/"+this.channelName+"/"; + archive["@id"] = "chat-messages/"+this.service.domain+"/users/"+this.channelName+"/"; } - archive["ircURI"] = this.server.ircURI+"/"+roomName; break; - case 'xmpp': - archive["xmppURI"] = `xmpp:${this.channelName}@${this.server.xmppMUC}`; + case 'XMPP': + // XMPP-specific adjustments break; } @@ -456,9 +448,11 @@ const ChatMessages = function (privateClient, publicClient) { } /* - * Method: _updatePreviousArchive - * * Finds the previous archive document and updates its today.next value + * + * @returns {boolean|Promise} + * + * @private */ _updatePreviousArchive () { return this._findPreviousArchive().then((archive) => { @@ -478,9 +472,11 @@ const ChatMessages = function (privateClient, publicClient) { } /* - * Method: _findPreviousArchive - * * Returns the previous archive document + * + * @returns {Promise} + * + * @private */ _findPreviousArchive () { const monthPath = this.path.substring(0, this.path.length-2); @@ -542,9 +538,11 @@ const ChatMessages = function (privateClient, publicClient) { } /* - * Method: _sync - * * Write archive document + * + * @returns {Promise} + * + * @private */ _sync (obj) { console.debug('[chat-messages] Writing archive object', obj);