commit 12196fdc6318442774be5cc100d1c97d00bcc0a5 Author: Sebastian Kippe Date: Wed May 3 18:42:26 2017 +0200 Port source from legacy module repo diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..2906c52 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,35 @@ +{ + "predef": [ + "global", + "Promise", + "require", + "process", + "module", + "exports" + ], + "browser": false, + "boss": true, + "curly": true, + "debug": false, + "devel": true, + "eqeqeq": true, + "evil": true, + "forin": false, + "immed": false, + "laxbreak": false, + "newcap": true, + "noarg": true, + "noempty": false, + "nonew": false, + "nomen": false, + "onevar": false, + "plusplus": false, + "regexp": false, + "undef": true, + "sub": true, + "strict": false, + "white": false, + "eqnull": true, + "esnext": true, + "unused": true +} diff --git a/dist/build.js b/dist/build.js new file mode 100644 index 0000000..71fc489 --- /dev/null +++ b/dist/build.js @@ -0,0 +1 @@ +!function(t,e){if("object"==typeof exports&&"object"==typeof module)module.exports=e(require("remotestoragejs"));else if("function"==typeof define&&define.amd)define(["remotestoragejs"],e);else{var n=e("object"==typeof exports?require("remotestoragejs"):t.RemoteStorage);for(var r in n)("object"==typeof exports?exports:t)[r]=n[r]}}(this,function(t){return function(t){function e(r){if(n[r])return n[r].exports;var i=n[r]={exports:{},id:r,loaded:!1};return t[r].call(i.exports,i,i.exports,e),i.loaded=!0,i.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";function r(t){if(Array.isArray(t)){for(var e=0,n=Array(t.length);e0){var h=o(Math.max.apply(Math,r(a)).toString());return t.client.getObject(e+h)}return t.client.getListing(n).then(function(e){var s=Object.keys(e).map(function(t){return parseInt(t.substr(0,2))}).map(function(e){return e0){var a=o(Math.max.apply(Math,r(s)).toString());return t.client.getListing(n+a+"/").then(function(e){var i=Object.keys(e).map(function(t){return parseInt(t)}),s=o(Math.max.apply(Math,r(i)).toString());return t.client.getObject(n+a+"/"+s)})}return t.client.getListing(i).then(function(e){var n=Object.keys(e).map(function(t){return parseInt(t.substr(0,4))}).map(function(e){return e0){var s=Math.max.apply(Math,r(n)).toString();return t.client.getListing(i+s+"/").then(function(e){var n=Object.keys(e).map(function(t){return parseInt(t.substr(0,2))}),a=o(Math.max.apply(Math,r(n)).toString());return t.client.getListing(i+s+"/"+a+"/").then(function(e){var n=Object.keys(e).map(function(t){return parseInt(t)}),h=o(Math.max.apply(Math,r(n)).toString());return t.client.getObject(i+s+"/"+a+"/"+h)})})}return!1})})})},_sync:function(t){return s.log("[chat-messages] Writing archive object",t),this.client.storeObject("daily-archive",this.path,t).then(function(){return s.log("[chat-messages] Archive written to remote storage"),!0},function(t){return console.log("[chat-messages] Error trying to store object",t),t})}};var o=function(t){return t=String(t),1===t.length&&(t="0"+t),t},h=function(t){return{year:t.getUTCFullYear(),month:o(t.getUTCMonth()+1),day:o(t.getUTCDate())}},c={DailyArchive:a,privateClient:t,publicClient:e};return{exports:c}})},function(e,n){e.exports=t}])}); \ No newline at end of file diff --git a/dist/build.js.map b/dist/build.js.map new file mode 100644 index 0000000..aa77f35 --- /dev/null +++ b/dist/build.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///webpack/bootstrap fea08dbeb235bc9f8782","webpack:///./index.js","webpack:///external {\"root\":\"RemoteStorage\",\"commonjs2\":\"remotestoragejs\",\"commonjs\":\"remotestoragejs\",\"amd\":\"remotestoragejs\"}"],"names":["RemoteStorage","require","defineModule","privateClient","publicClient","archiveSchema","declareType","DailyArchive","options","isPublic","server","type","name","channelName","date","Date","parsedDate","parseDate","dateId","year","month","day","match","replace","path","client","previous","next","prototype","addMessage","message","Promise","resolve","getObject","then","archive","_updateDocument","_createDocument","addMessages","messages","overwrite","forEach","remove","log","Array","isArray","today","push","_sync","_buildArchiveObject","_updatePreviousArchive","roomName","ircURI","xmppMUC","_findPreviousArchive","substring","length","storeObject","monthPath","yearPath","basePath","getListing","listing","days","Object","keys","map","i","parseInt","filter","pad","Math","max","toString","months","substr","years","obj","error","console","num","String","getUTCFullYear","getUTCMonth","getUTCDate","exports"],"mappings":"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","file":"/home/basti/src/remotestorage/modules/remotestorage-module-chat-messages/dist/build.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"remotestoragejs\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"remotestoragejs\"], factory);\n\telse {\n\t\tvar a = typeof exports === 'object' ? factory(require(\"remotestoragejs\")) : factory(root[\"RemoteStorage\"]);\n\t\tfor(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];\n\t}\n})(this, function(__WEBPACK_EXTERNAL_MODULE_1__) {\nreturn \n\n\n// WEBPACK FOOTER //\n// webpack/universalModuleDefinition"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap fea08dbeb235bc9f8782","var RemoteStorage = require('remotestoragejs');\n\nRemoteStorage.defineModule(\"chat-messages\", function (privateClient, publicClient) {\n\n /**\n * Schema: chat-messages/daily\n *\n * Represents one day of chat messages\n *\n * Example:\n *\n * (start code)\n * {\n * \"@context\": \"https://kosmos.org/ns/v1\",\n * \"@id\": \"chat-messages/freenode/channels/kosmos/\",\n * \"@type\": \"ChatChannel\",\n * \"name\": \"#kosmos\",\n * \"ircURI\": \"irc://irc.freenode.net/kosmos\",\n * \"today\": {\n * \"@id\": \"2015/01/01\",\n * \"@type\": \"ChatLog\",\n * \"messageType\": \"InstantMessage\",\n * \"previous\": \"2014/12/31\",\n * \"next\": \"2015/01/02\",\n * \"messages\": [\n * { \"date\": \"2015-06-05T17:35:28.454Z\", \"user\": \"hal8000\", \"text\": \"knock knock\" },\n * { \"date\": \"2015-06-05T17:37:42.123Z\", \"user\": \"raucao\", \"text\": \"who's there?\" },\n * { \"date\": \"2015-06-05T17:55:01.235Z\", \"user\": \"hal8000\", \"text\": \"HAL\" }\n * ]\n * }\n * }\n * (end code)\n */\n\n const archiveSchema = {\n \"type\": \"object\",\n \"properties\": {\n \"@context\": {\n \"type\": \"string\",\n \"default\": \"https://kosmos.org/ns/v1\",\n \"enum\": [\"https://kosmos.org/ns/v1\"]\n },\n \"@id\": {\n \"type\": \"string\",\n \"required\": true\n },\n \"@type\": {\n \"type\": \"string\",\n \"default\": \"ChatChannel\",\n \"enum\": [\"ChatChannel\"]\n },\n \"name\": {\n \"type\": \"string\",\n \"required\": true\n },\n \"ircURI\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"xmppURI\": {\n \"type\": \"string\",\n \"format\": \"uri\"\n },\n \"today\": {\n \"type\": \"object\",\n \"properties\": {\n \"@id\": {\n \"type\": \"string\",\n \"pattern\": \"^[0-9]{4}\\/[0-9]{2}\\/[0-9]{2}$\",\n \"required\": true\n },\n \"@type\": {\n \"type\": \"string\",\n \"default\": \"ChatLog\",\n \"pattern\": \"^ChatLog$\"\n },\n \"messageType\": {\n \"type\": \"string\",\n \"default\": \"InstantMessage\",\n \"pattern\": \"^InstantMessage$\"\n },\n \"previous\": {\n \"type\": \"string\",\n \"pattern\": \"^[0-9]{4}\\/[0-9]{2}\\/[0-9]{2}$\"\n },\n \"next\": {\n \"type\": \"string\",\n \"pattern\": \"^[0-9]{4}\\/[0-9]{2}\\/[0-9]{2}$\"\n },\n \"messages\": {\n \"type\": \"array\",\n \"required\": true,\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"date\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n },\n \"user\": {\n \"type\": \"string\"\n },\n \"text\": {\n \"type\": \"string\"\n },\n \"type\": \"string\",\n \"default\": \"text\",\n \"enum\": [\n \"text\",\n \"join\",\n \"leave\",\n \"action\"\n ]\n }\n }\n }\n }\n }\n },\n \"required\": []\n };\n\n privateClient.declareType(\"daily-archive\", \"https://kosmos.org/ns/v1\", archiveSchema);\n publicClient.declareType(\"daily-archive\", \"https://kosmos.org/ns/v1\", archiveSchema);\n\n /**\n * Class: DailyArchive\n *\n * A daily archive stores IRC messages by day.\n *\n * Parameters (object):\n * server - Chat server info (see )\n * channelName - Name of room/channel\n * date - Date of archive day\n * isPublic - Store logs in public folder (defaults to false)\n * previous - Date of previous log file as YYYY/MM/DD;\n * looked up automatically when not given\n * next - Date of next log file as YYYY/MM/DD;\n * looked up automatically when not given\n *\n * Example for IRC:\n *\n * (start code)\n * var archive = new chatMessages.DailyArchive({\n * server: {\n * type: 'irc',\n * name: 'freenode',\n * ircURI: 'irc://irc.freenode.net'\n * },\n * channelName: '#kosmos',\n * date: new Date(),\n * isPublic: true\n * });\n * (end code)\n *\n * Example for XMPP:\n *\n * (start code)\n * var archive = new chatMessages.DailyArchive({\n * server: {\n * type: 'xmpp',\n * name: '5apps',\n * xmppMUC: 'muc.5apps.com'\n * },\n * channelName: 'watercooler',\n * date: new Date(),\n * isPublic: false\n * });\n * (end code)\n */\n var DailyArchive = function DailyArchive(options) {\n //\n // Defaults\n //\n options.isPublic = options.isPublic || false;\n\n //\n // Validate options\n //\n if (typeof options !== \"object\") {\n throw \"options must be an object\";\n }\n if (typeof options.server !== \"object\" ||\n typeof options.server.type !== \"string\" ||\n typeof options.server.name !== \"string\") {\n throw \"server must be an object containing at least server \\\"type\\\" and \\\"name\\\"\";\n }\n if (typeof options.channelName !== \"string\") {\n throw \"channelName must be a string\";\n }\n if (!(options.date instanceof Date)) {\n throw \"date must be a date object\";\n }\n if (typeof options.isPublic !== \"boolean\") {\n throw \"isPublic must be a boolean value\";\n }\n\n /**\n * Property: server\n *\n * Contains information about the chat server/network\n *\n * Properties:\n * type - Type of server/protocol (e.g. \"irc\", \"xmpp\", \"campfire\", \"slack\")\n * name - Shortname/id/alias of network/server (e.g. \"freenode\", \"mycompanyname\")\n * ircURI - (optional) IRC URI of network (e.g. \"irc://irc.freenode.net/\")\n * xmppMUC - (optional) XMPP MUC service host (e.g. \"conference.jabber.org\")\n */\n this.server = options.server;\n\n /**\n * Property: channelName\n *\n * Name of the IRC channel (e.g. \"#kosmos\")\n */\n this.channelName = options.channelName;\n\n /**\n * Property: date\n *\n * Date of the archive's content\n */\n this.date = options.date;\n\n /**\n * Property: isPublic\n *\n * `true` for public archives, `false` for private ones\n */\n this.isPublic = options.isPublic;\n\n /**\n * Property: parsedDate\n *\n * Object containing padded year, month and day of date\n */\n this.parsedDate = parseDate(this.date);\n\n /**\n * Property: dateId\n *\n * Date string in the form of YYYY/MM/DD\n */\n this.dateId = this.parsedDate.year+'/'+this.parsedDate.month+'/'+this.parsedDate.day;\n\n /**\n * Property: path\n *\n * Document path of the archive file\n */\n switch (this.server.type) {\n case 'irc':\n if (this.channelName.match(/^#/)) {\n // normal chatroom\n var channelName = this.channelName.replace(/^#/,'');\n this.path = `${this.server.name}/channels/${channelName}/${this.dateId}`;\n } else {\n // user direct message\n this.path = `${this.server.name}/users/${this.channelName}/${this.dateId}`;\n }\n break;\n default:\n this.path = `${this.server.name}/${this.channelName}/${this.dateId}`;\n break;\n }\n\n /**\n * Property: client\n *\n * Public or private BaseClient, depending on isPublic\n */\n this.client = this.isPublic ? publicClient : privateClient;\n\n /**\n * Property: previous\n *\n * Date of previous log file as YYYY/MM/DD\n */\n this.previous = options.previous;\n\n /**\n * Property: next\n *\n * Date of next log file as YYYY/MM/DD\n */\n this.next = options.next;\n };\n\n DailyArchive.prototype = {\n /*\n * Method: addMessage\n *\n * Parameters (object):\n * timestamp - Timestamp of the message\n * from - The sender of the message\n * text - The message itself\n * type - Type of message (one of text, join, leave, action)\n */\n addMessage: function addMessage(message) {\n if (this.isPublic && !this.channelName.match(/^#/)) {\n return Promise.resolve(false);\n }\n\n message.type = message.type || 'text';\n\n return this.client.getObject(this.path).then((archive) => {\n if (typeof archive === 'object') {\n return this._updateDocument(archive, message);\n } else {\n return this._createDocument(message);\n }\n });\n },\n\n /*\n * Method: addMessages\n *\n * Like , but for multiple messages at once. Useful for bulk\n * imports of messages.\n *\n * Parameters:\n * messages - Array of message objects (see params for addMessage)\n * overwrite - If true, creates a new archive file and overwrites the\n * old one. Defaults to false.\n */\n addMessages: function addMessage(messages, overwrite) {\n if (this.isPublic && !this.channelName.match(/^#/)) {\n return Promise.resolve(false);\n }\n\n overwrite = overwrite || false;\n\n messages.forEach(function(message) {\n message.type = message.type || 'text';\n });\n\n if (overwrite) {\n return this._createDocument(messages);\n } else {\n return this.client.getObject(this.path).then((archive) => {\n if (typeof archive === 'object') {\n return this._updateDocument(archive, messages);\n } else {\n return this._createDocument(messages);\n }\n });\n }\n },\n\n /*\n * Method: remove\n *\n * Deletes the entire archive document from storage\n */\n remove: function() {\n return this.client.remove(this.path);\n },\n\n /*\n * Method: _updateDocument\n *\n * Updates and writes an existing archive document\n */\n _updateDocument: function(archive, messages) {\n RemoteStorage.log('[chat-messages] Updating archive document', archive);\n\n if (Array.isArray(messages)) {\n messages.forEach(function(message) {\n archive.today.messages.push(message);\n });\n } else {\n archive.today.messages.push(messages);\n }\n\n return this._sync(archive);\n },\n\n /*\n * Method: _createDocument\n *\n * Creates and writes a new archive document\n */\n _createDocument: function(messages) {\n RemoteStorage.log('[chat-messages] Creating new archive document');\n let archive = this._buildArchiveObject();\n\n if (Array.isArray(messages)) {\n messages.forEach((message) => {\n archive.today.messages.push(message);\n });\n } else {\n archive.today.messages.push(messages);\n }\n\n if (this.previous || this.next) {\n // The app is handling previous/next keys itself\n // That includes setting 'next' in the previous log file\n if (this.previous) { archive.today.previous = this.previous; }\n if (this.next) { archive.today.next = this.next; }\n return this._sync(archive);\n } else {\n // Find and update previous archive, set 'previous' on this one\n return this._updatePreviousArchive().then((previous) => {\n if (typeof previous === 'object') {\n archive.today.previous = previous.today['@id'];\n }\n return this._sync(archive);\n });\n }\n },\n\n /*\n * Method: _buildArchiveObject\n *\n * Builds the object to be stored in remote storage\n */\n _buildArchiveObject: function() {\n let roomName = this.channelName.replace(/#/,'');\n\n let archive = {\n \"@id\": \"chat-messages/\"+this.server.name+\"/channels/\"+roomName+\"/\",\n \"@type\": \"ChatChannel\",\n \"name\": this.channelName,\n \"today\": {\n \"@id\": this.dateId,\n \"@type\": \"ChatLog\",\n \"messageType\": \"InstantMessage\",\n \"messages\": []\n }\n };\n\n switch (this.server.type) {\n case 'irc':\n if (!this.channelName.match(/^#/)) {\n archive[\"@id\"] = \"chat-messages/\"+this.server.name+\"/users/\"+this.channelName+\"/\";\n }\n archive[\"ircURI\"] = this.server.ircURI+\"/\"+roomName;\n break;\n case 'xmpp':\n archive[\"xmppURI\"] = `xmpp:${this.channelName}@${this.server.xmppMUC}`;\n break;\n }\n\n return archive;\n },\n\n /*\n * Method: _updatePreviousArchive\n *\n * Finds the previous archive document and updates its today.next value\n */\n _updatePreviousArchive: function() {\n return this._findPreviousArchive().then((archive) => {\n if (typeof archive === 'object' && archive.today) {\n archive.today.next = this.dateId;\n let path = this.path.substring(0, this.path.length-this.dateId.length)+archive.today['@id'];\n\n return this.client.storeObject('daily-archive', path, archive).then(() => {\n RemoteStorage.log('[chat-messages] Previous archive written to remote storage', path, archive);\n return archive;\n });\n } else {\n RemoteStorage.log('[chat-messages] Previous archive not found');\n return false;\n }\n });\n },\n\n /*\n * Method: _findPreviousArchive\n *\n * Returns the previous archive document\n */\n _findPreviousArchive: function() {\n const monthPath = this.path.substring(0, this.path.length-2);\n const yearPath = this.path.substring(0, this.path.length-5);\n const basePath = this.path.substring(0, this.path.length-10);\n\n return this.client.getListing(monthPath).then((listing) => {\n let days = Object.keys(listing).map((i) => parseInt(i)).map((i) => {\n return (i < parseInt(this.parsedDate.day)) ? i : null;\n }).filter(function(i){ return i != null; });\n\n if (days.length > 0) {\n let day = pad(Math.max(...days).toString());\n return this.client.getObject(monthPath+day);\n }\n\n // Find last day in previous month\n return this.client.getListing(yearPath).then((listing) => {\n let months = Object.keys(listing).map((i) => parseInt(i.substr(0,2))).map((i) => {\n return (i < parseInt(this.parsedDate.month)) ? i : null;\n }).filter(function(i){ return i != null; });\n\n if (months.length > 0) {\n let month = pad(Math.max(...months).toString());\n\n return this.client.getListing(yearPath+month+'/').then((listing) => {\n let days = Object.keys(listing).map((i) => parseInt(i));\n let day = pad(Math.max(...days).toString());\n return this.client.getObject(yearPath+month+'/'+day);\n });\n } else {\n // Find last month and day in previous year\n return this.client.getListing(basePath).then((listing) => {\n\n let years = Object.keys(listing).map((i) => parseInt(i.substr(0,4))).map((i) => {\n return (i < parseInt(this.parsedDate.year)) ? i : null;\n }).filter(function(i){ return i != null; });\n\n if (years.length > 0) {\n let year = Math.max(...years).toString();\n\n return this.client.getListing(basePath+year+'/').then((listing) => {\n let months = Object.keys(listing).map((i) => parseInt(i.substr(0,2)));\n let month = pad(Math.max(...months).toString());\n\n return this.client.getListing(basePath+year+'/'+month+'/').then((listing) => {\n let days = Object.keys(listing).map((i) => parseInt(i));\n let day = pad(Math.max(...days).toString());\n return this.client.getObject(basePath+year+'/'+month+'/'+day);\n });\n });\n } else {\n return false;\n }\n });\n }\n });\n });\n },\n\n /*\n * Method: _sync\n *\n * Write archive document\n */\n _sync: function(obj) {\n RemoteStorage.log('[chat-messages] Writing archive object', obj);\n\n return this.client.storeObject('daily-archive', this.path, obj).then(function(){\n RemoteStorage.log('[chat-messages] Archive written to remote storage');\n return true;\n },function(error){\n console.log('[chat-messages] Error trying to store object', error);\n return error;\n });\n }\n };\n\n var pad = function(num) {\n num = String(num);\n if (num.length === 1) { num = \"0\" + num; }\n return num;\n };\n\n var parseDate = function(date) {\n return {\n year: date.getUTCFullYear(),\n month: pad( date.getUTCMonth() + 1 ),\n day: pad( date.getUTCDate() )\n };\n };\n\n var exports = {\n DailyArchive: DailyArchive,\n privateClient: privateClient,\n publicClient: publicClient\n };\n\n // Return public functions\n return { exports: exports };\n});\n\n\n\n// WEBPACK FOOTER //\n// ./index.js","module.exports = __WEBPACK_EXTERNAL_MODULE_1__;\n\n\n//////////////////\n// WEBPACK FOOTER\n// external {\"root\":\"RemoteStorage\",\"commonjs2\":\"remotestoragejs\",\"commonjs\":\"remotestoragejs\",\"amd\":\"remotestoragejs\"}\n// module id = 1\n// module chunks = 0"],"sourceRoot":""} \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..54592a3 --- /dev/null +++ b/index.js @@ -0,0 +1,573 @@ +var RemoteStorage = require('remotestoragejs'); + +RemoteStorage.defineModule("chat-messages", function (privateClient, publicClient) { + + /** + * Schema: chat-messages/daily + * + * Represents one day of chat messages + * + * Example: + * + * (start code) + * { + * "@context": "https://kosmos.org/ns/v1", + * "@id": "chat-messages/freenode/channels/kosmos/", + * "@type": "ChatChannel", + * "name": "#kosmos", + * "ircURI": "irc://irc.freenode.net/kosmos", + * "today": { + * "@id": "2015/01/01", + * "@type": "ChatLog", + * "messageType": "InstantMessage", + * "previous": "2014/12/31", + * "next": "2015/01/02", + * "messages": [ + * { "date": "2015-06-05T17:35:28.454Z", "user": "hal8000", "text": "knock knock" }, + * { "date": "2015-06-05T17:37:42.123Z", "user": "raucao", "text": "who's there?" }, + * { "date": "2015-06-05T17:55:01.235Z", "user": "hal8000", "text": "HAL" } + * ] + * } + * } + * (end code) + */ + + const archiveSchema = { + "type": "object", + "properties": { + "@context": { + "type": "string", + "default": "https://kosmos.org/ns/v1", + "enum": ["https://kosmos.org/ns/v1"] + }, + "@id": { + "type": "string", + "required": true + }, + "@type": { + "type": "string", + "default": "ChatChannel", + "enum": ["ChatChannel"] + }, + "name": { + "type": "string", + "required": true + }, + "ircURI": { + "type": "string", + "format": "uri" + }, + "xmppURI": { + "type": "string", + "format": "uri" + }, + "today": { + "type": "object", + "properties": { + "@id": { + "type": "string", + "pattern": "^[0-9]{4}\/[0-9]{2}\/[0-9]{2}$", + "required": true + }, + "@type": { + "type": "string", + "default": "ChatLog", + "pattern": "^ChatLog$" + }, + "messageType": { + "type": "string", + "default": "InstantMessage", + "pattern": "^InstantMessage$" + }, + "previous": { + "type": "string", + "pattern": "^[0-9]{4}\/[0-9]{2}\/[0-9]{2}$" + }, + "next": { + "type": "string", + "pattern": "^[0-9]{4}\/[0-9]{2}\/[0-9]{2}$" + }, + "messages": { + "type": "array", + "required": true, + "items": { + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date-time" + }, + "user": { + "type": "string" + }, + "text": { + "type": "string" + }, + "type": "string", + "default": "text", + "enum": [ + "text", + "join", + "leave", + "action" + ] + } + } + } + } + } + }, + "required": [] + }; + + privateClient.declareType("daily-archive", "https://kosmos.org/ns/v1", archiveSchema); + publicClient.declareType("daily-archive", "https://kosmos.org/ns/v1", archiveSchema); + + /** + * Class: DailyArchive + * + * 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) + * var 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) + * var archive = new chatMessages.DailyArchive({ + * server: { + * type: 'xmpp', + * name: '5apps', + * xmppMUC: 'muc.5apps.com' + * }, + * channelName: 'watercooler', + * date: new Date(), + * isPublic: false + * }); + * (end code) + */ + var DailyArchive = function DailyArchive(options) { + // + // Defaults + // + options.isPublic = options.isPublic || false; + + // + // Validate options + // + 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.channelName !== "string") { + throw "channelName must be a string"; + } + if (!(options.date instanceof Date)) { + throw "date must be a date object"; + } + if (typeof options.isPublic !== "boolean") { + throw "isPublic must be a boolean value"; + } + + /** + * 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") + */ + this.server = options.server; + + /** + * Property: channelName + * + * Name of the IRC channel (e.g. "#kosmos") + */ + this.channelName = options.channelName; + + /** + * Property: date + * + * Date of the archive's content + */ + this.date = options.date; + + /** + * Property: isPublic + * + * `true` for public archives, `false` for private ones + */ + this.isPublic = options.isPublic; + + /** + * Property: parsedDate + * + * Object containing padded year, month and day of date + */ + this.parsedDate = parseDate(this.date); + + /** + * Property: 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 + */ + switch (this.server.type) { + case 'irc': + if (this.channelName.match(/^#/)) { + // normal chatroom + var 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; + } + + /** + * Property: client + * + * Public or private BaseClient, depending on isPublic + */ + this.client = this.isPublic ? publicClient : privateClient; + + /** + * Property: 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 + */ + this.next = options.next; + }; + + DailyArchive.prototype = { + /* + * Method: addMessage + * + * 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) + */ + addMessage: function addMessage(message) { + if (this.isPublic && !this.channelName.match(/^#/)) { + return Promise.resolve(false); + } + + message.type = message.type || 'text'; + + return this.client.getObject(this.path).then((archive) => { + if (typeof archive === 'object') { + return this._updateDocument(archive, message); + } else { + return this._createDocument(message); + } + }); + }, + + /* + * 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. + */ + addMessages: function addMessage(messages, overwrite) { + if (this.isPublic && !this.channelName.match(/^#/)) { + return Promise.resolve(false); + } + + overwrite = overwrite || false; + + messages.forEach(function(message) { + message.type = message.type || 'text'; + }); + + if (overwrite) { + return this._createDocument(messages); + } else { + return this.client.getObject(this.path).then((archive) => { + if (typeof archive === 'object') { + return this._updateDocument(archive, messages); + } else { + return this._createDocument(messages); + } + }); + } + }, + + /* + * Method: remove + * + * Deletes the entire archive document from storage + */ + remove: function() { + return this.client.remove(this.path); + }, + + /* + * Method: _updateDocument + * + * Updates and writes an existing archive document + */ + _updateDocument: function(archive, messages) { + RemoteStorage.log('[chat-messages] Updating archive document', archive); + + if (Array.isArray(messages)) { + messages.forEach(function(message) { + archive.today.messages.push(message); + }); + } else { + archive.today.messages.push(messages); + } + + return this._sync(archive); + }, + + /* + * Method: _createDocument + * + * Creates and writes a new archive document + */ + _createDocument: function(messages) { + RemoteStorage.log('[chat-messages] Creating new archive document'); + let archive = this._buildArchiveObject(); + + if (Array.isArray(messages)) { + messages.forEach((message) => { + archive.today.messages.push(message); + }); + } else { + archive.today.messages.push(messages); + } + + if (this.previous || this.next) { + // The app is handling previous/next keys itself + // That includes setting 'next' in the previous log file + if (this.previous) { archive.today.previous = this.previous; } + if (this.next) { archive.today.next = this.next; } + return this._sync(archive); + } else { + // Find and update previous archive, set 'previous' on this one + return this._updatePreviousArchive().then((previous) => { + if (typeof previous === 'object') { + archive.today.previous = previous.today['@id']; + } + return this._sync(archive); + }); + } + }, + + /* + * Method: _buildArchiveObject + * + * Builds the object to be stored in remote storage + */ + _buildArchiveObject: function() { + let roomName = this.channelName.replace(/#/,''); + + let archive = { + "@id": "chat-messages/"+this.server.name+"/channels/"+roomName+"/", + "@type": "ChatChannel", + "name": this.channelName, + "today": { + "@id": this.dateId, + "@type": "ChatLog", + "messageType": "InstantMessage", + "messages": [] + } + }; + + switch (this.server.type) { + case 'irc': + if (!this.channelName.match(/^#/)) { + archive["@id"] = "chat-messages/"+this.server.name+"/users/"+this.channelName+"/"; + } + archive["ircURI"] = this.server.ircURI+"/"+roomName; + break; + case 'xmpp': + archive["xmppURI"] = `xmpp:${this.channelName}@${this.server.xmppMUC}`; + break; + } + + return archive; + }, + + /* + * Method: _updatePreviousArchive + * + * Finds the previous archive document and updates its today.next value + */ + _updatePreviousArchive: function() { + return this._findPreviousArchive().then((archive) => { + if (typeof archive === 'object' && archive.today) { + archive.today.next = this.dateId; + let path = this.path.substring(0, this.path.length-this.dateId.length)+archive.today['@id']; + + return this.client.storeObject('daily-archive', path, archive).then(() => { + RemoteStorage.log('[chat-messages] Previous archive written to remote storage', path, archive); + return archive; + }); + } else { + RemoteStorage.log('[chat-messages] Previous archive not found'); + return false; + } + }); + }, + + /* + * Method: _findPreviousArchive + * + * Returns the previous archive document + */ + _findPreviousArchive: function() { + const monthPath = this.path.substring(0, this.path.length-2); + const yearPath = this.path.substring(0, this.path.length-5); + const basePath = this.path.substring(0, this.path.length-10); + + return this.client.getListing(monthPath).then((listing) => { + let days = Object.keys(listing).map((i) => parseInt(i)).map((i) => { + return (i < parseInt(this.parsedDate.day)) ? i : null; + }).filter(function(i){ return i != null; }); + + if (days.length > 0) { + let day = pad(Math.max(...days).toString()); + return this.client.getObject(monthPath+day); + } + + // Find last day in previous month + return this.client.getListing(yearPath).then((listing) => { + let months = Object.keys(listing).map((i) => parseInt(i.substr(0,2))).map((i) => { + return (i < parseInt(this.parsedDate.month)) ? i : null; + }).filter(function(i){ return i != null; }); + + if (months.length > 0) { + let month = pad(Math.max(...months).toString()); + + return this.client.getListing(yearPath+month+'/').then((listing) => { + let days = Object.keys(listing).map((i) => parseInt(i)); + let day = pad(Math.max(...days).toString()); + return this.client.getObject(yearPath+month+'/'+day); + }); + } else { + // Find last month and day in previous year + return this.client.getListing(basePath).then((listing) => { + + let years = Object.keys(listing).map((i) => parseInt(i.substr(0,4))).map((i) => { + return (i < parseInt(this.parsedDate.year)) ? i : null; + }).filter(function(i){ return i != null; }); + + if (years.length > 0) { + let year = Math.max(...years).toString(); + + return this.client.getListing(basePath+year+'/').then((listing) => { + let months = Object.keys(listing).map((i) => parseInt(i.substr(0,2))); + let month = pad(Math.max(...months).toString()); + + return this.client.getListing(basePath+year+'/'+month+'/').then((listing) => { + let days = Object.keys(listing).map((i) => parseInt(i)); + let day = pad(Math.max(...days).toString()); + return this.client.getObject(basePath+year+'/'+month+'/'+day); + }); + }); + } else { + return false; + } + }); + } + }); + }); + }, + + /* + * Method: _sync + * + * Write archive document + */ + _sync: function(obj) { + RemoteStorage.log('[chat-messages] Writing archive object', obj); + + return this.client.storeObject('daily-archive', this.path, obj).then(function(){ + RemoteStorage.log('[chat-messages] Archive written to remote storage'); + return true; + },function(error){ + console.log('[chat-messages] Error trying to store object', error); + return error; + }); + } + }; + + var pad = function(num) { + num = String(num); + if (num.length === 1) { num = "0" + num; } + return num; + }; + + var parseDate = function(date) { + return { + year: date.getUTCFullYear(), + month: pad( date.getUTCMonth() + 1 ), + day: pad( date.getUTCDate() ) + }; + }; + + var exports = { + DailyArchive: DailyArchive, + privateClient: privateClient, + publicClient: publicClient + }; + + // Return public functions + return { exports: exports }; +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..64dd923 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "remotestorage-module-chat-messages", + "version": "0.6.0", + "description": "Stores chat messages in daily archive files", + "main": "./dist/build.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "webpack -w", + "build": "NODE_ENV=production webpack" + }, + "author": "Kosmos Developers (https://kosmos.org)", + "contributors": [ + "Sebastian Kippe " + ], + "license": "MIT", + "devDependencies": { + "babel-core": "^6.18.2", + "babel-loader": "^6.2.7", + "babel-preset-es2015": "^6.18.0", + "webpack": "^1.13.2" + } +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..7cc83aa --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,32 @@ +var webpack = require('webpack'); +var isProd = (process.env.NODE_ENV === 'production'); + +// minimize only in production +var plugins = isProd ? [new webpack.optimize.UglifyJsPlugin({minimize: true })] : [] + +module.exports = { + entry: './index.js', + // source map not in production + devtool: !isProd && 'source-map', + output: { + filename: __dirname + '/dist/build.js', + libraryTarget: 'umd' + }, + externals: { + "remotestoragejs": { + root: "RemoteStorage", //