Compare commits
34
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcf117e1dd
|
||
|
|
a0bec9a12c
|
||
|
|
189cd1f86d
|
||
|
|
8db448c4a0
|
||
|
|
84613ed3a3 | ||
|
|
f83ef92adc
|
||
|
|
54352e09e8
|
||
|
|
cd93cd4167
|
||
|
|
9f477ba14b
|
||
|
|
328be7ed75
|
||
|
|
cf8c43bede | ||
|
|
188b45e778
|
||
|
|
d331af7256 | ||
|
|
9ba618f38e
|
||
|
|
0b517400b1 | ||
|
|
15af899e1d | ||
|
|
bf896a076c | ||
|
|
a5a547d40d
|
||
|
|
21cb5a02c8 | ||
|
|
c42e0a37a8
|
||
|
|
2a73816e56 | ||
|
|
74680ad42b | ||
|
|
dd1bfd08ea
|
||
|
|
e803a1e799
|
||
|
|
54f5d903d0
|
||
|
|
a270d5f9d6 | ||
|
|
603fd507bf
|
||
|
|
6b95652a76
|
||
|
|
89c4ee229a | ||
|
|
990b081b3d | ||
|
|
4ce2b90351 | ||
|
|
2a22d0f399 | ||
|
|
2ff10577c0 | ||
|
|
989b8436c2 |
No files matched your search
@@ -0,0 +1,40 @@
|
||||
[](https://www.npmjs.com/package/remotestorage-module-chat-messages)
|
||||
|
||||
# remoteStorage Module: Chat Messages
|
||||
|
||||
Stores chat messages in daily archive documents.
|
||||
|
||||
## Usage
|
||||
|
||||
Open a daily archive and write messages to it:
|
||||
|
||||
```js
|
||||
const RemoteStorage = require("remotestoragejs");
|
||||
const ChatMessages = require("remotestorage-module-chat-messages");
|
||||
const remoteStorage = new RemoteStorage({ modules: [ ChatMessages ] });
|
||||
|
||||
const archive = new remoteStorage.chatMessages.DailyArchive({
|
||||
service: {
|
||||
protocol: 'IRC',
|
||||
domain: 'irc.libera.chat'
|
||||
},
|
||||
channelName: '#kosmos',
|
||||
date: new Date(),
|
||||
isPublic: true // Channel logs will be written to public folder
|
||||
});
|
||||
|
||||
const messages = [
|
||||
{ "date": "2015-06-05T17:35:28.454Z", "user": "jimmy", "text": "knock knock" },
|
||||
{ "date": "2015-06-05T17:36:05.123Z", "user": "walter", "text": "who's there?" }
|
||||
];
|
||||
|
||||
archive.addMessages(messages);
|
||||
```
|
||||
|
||||
See the inline source code documentation (JSDoc) for usage details and function
|
||||
arguments. For a real-world integration example, see
|
||||
[hubot-remotestorage-logger](https://github.com/67P/hubot-remotestorage-logger/).
|
||||
|
||||
## Support, bugs, feedback, questions
|
||||
|
||||
Come and chat with us: https://wiki.kosmos.org/Main_Page#Chat
|
||||
Vendored
+2
-1
@@ -1 +1,2 @@
|
||||
!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);e<t.length;e++)n[e]=t[e];return n}return Array.from(t)}var i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},s=n(1);s.defineModule("chat-messages",function(t,e){var n={type:"object",properties:{"@context":{type:"string",default:"https://kosmos.org/ns/v1",enum:["https://kosmos.org/ns/v1"]},"@id":{type:"string",required:!0},"@type":{type:"string",default:"ChatChannel",enum:["ChatChannel"]},name:{type:"string",required:!0},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:!0},"@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:!0,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:[]};t.declareType("daily-archive","https://kosmos.org/ns/v1",n),e.declareType("daily-archive","https://kosmos.org/ns/v1",n);var a=function(n){if(n.isPublic=n.isPublic||!1,"object"!==("undefined"==typeof n?"undefined":i(n)))throw"options must be an object";if("object"!==i(n.server)||"string"!=typeof n.server.type||"string"!=typeof n.server.name)throw'server must be an object containing at least server "type" and "name"';if("string"!=typeof n.channelName)throw"channelName must be a string";if(!(n.date instanceof Date))throw"date must be a date object";if("boolean"!=typeof n.isPublic)throw"isPublic must be a boolean value";switch(this.server=n.server,this.channelName=n.channelName,this.date=n.date,this.isPublic=n.isPublic,this.parsedDate=h(this.date),this.dateId=this.parsedDate.year+"/"+this.parsedDate.month+"/"+this.parsedDate.day,this.server.type){case"irc":if(this.channelName.match(/^#/)){var r=this.channelName.replace(/^#/,"");this.path=this.server.name+"/channels/"+r+"/"+this.dateId}else this.path=this.server.name+"/users/"+this.channelName+"/"+this.dateId;break;default:this.path=this.server.name+"/"+this.channelName+"/"+this.dateId}this.client=this.isPublic?e:t,this.previous=n.previous,this.next=n.next};a.prototype={addMessage:function(t){var e=this;return this.isPublic&&!this.channelName.match(/^#/)?Promise.resolve(!1):(t.type=t.type||"text",this.client.getObject(this.path).then(function(n){return"object"===("undefined"==typeof n?"undefined":i(n))?e._updateDocument(n,t):e._createDocument(t)}))},addMessages:function(t,e){var n=this;return this.isPublic&&!this.channelName.match(/^#/)?Promise.resolve(!1):(e=e||!1,t.forEach(function(t){t.type=t.type||"text"}),e?this._createDocument(t):this.client.getObject(this.path).then(function(e){return"object"===("undefined"==typeof e?"undefined":i(e))?n._updateDocument(e,t):n._createDocument(t)}))},remove:function(){return this.client.remove(this.path)},_updateDocument:function(t,e){return s.log("[chat-messages] Updating archive document",t),Array.isArray(e)?e.forEach(function(e){t.today.messages.push(e)}):t.today.messages.push(e),this._sync(t)},_createDocument:function(t){var e=this;s.log("[chat-messages] Creating new archive document");var n=this._buildArchiveObject();return Array.isArray(t)?t.forEach(function(t){n.today.messages.push(t)}):n.today.messages.push(t),this.previous||this.next?(this.previous&&(n.today.previous=this.previous),this.next&&(n.today.next=this.next),this._sync(n)):this._updatePreviousArchive().then(function(t){return"object"===("undefined"==typeof t?"undefined":i(t))&&(n.today.previous=t.today["@id"]),e._sync(n)})},_buildArchiveObject:function(){var t=this.channelName.replace(/#/,""),e={"@id":"chat-messages/"+this.server.name+"/channels/"+t+"/","@type":"ChatChannel",name:this.channelName,today:{"@id":this.dateId,"@type":"ChatLog",messageType:"InstantMessage",messages:[]}};switch(this.server.type){case"irc":this.channelName.match(/^#/)||(e["@id"]="chat-messages/"+this.server.name+"/users/"+this.channelName+"/"),e.ircURI=this.server.ircURI+"/"+t;break;case"xmpp":e.xmppURI="xmpLine truncated
|
||||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define("ChatMessages",[],e):"object"==typeof exports?exports.ChatMessages=e():t.ChatMessages=e()}(this,(function(){return(()=>{var t={666:t=>{var e=function(t){"use strict";var e,r=Object.prototype,n=r.hasOwnProperty,a="function"==typeof Symbol?Symbol:{},i=a.iterator||"@@iterator",o=a.asyncIterator||"@@asyncIterator",s=a.toStringTag||"@@toStringTag";function c(t,e,r){return Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{c({},"")}catch(t){c=function(t,e,r){return t[e]=r}}function u(t,e,r,n){var a=e&&e.prototype instanceof d?e:d,i=Object.create(a.prototype),o=new M(n||[]);return i._invoke=function(t,e,r){var n=l;return function(a,i){if(n===f)throw new Error("Generator is already running");if(n===y){if("throw"===a)throw i;return E()}for(r.method=a,r.arg=i;;){var o=r.delegate;if(o){var s=P(o,r);if(s){if(s===m)continue;return s}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if(n===l)throw n=y,r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n=f;var c=h(t,e,r);if("normal"===c.type){if(n=r.done?y:p,c.arg===m)continue;return{value:c.arg,done:r.done}}"throw"===c.type&&(n=y,r.method="throw",r.arg=c.arg)}}}(t,r,o),i}function h(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}t.wrap=u;var l="suspendedStart",p="suspendedYield",f="executing",y="completed",m={};function d(){}function g(){}function v(){}var b={};c(b,i,(function(){return this}));var w=Object.getPrototypeOf,x=w&&w(w(C([])));x&&x!==r&&n.call(x,i)&&(b=x);var j=v.prototype=d.prototype=Object.create(b);function k(t){["next","throw","return"].forEach((function(e){c(t,e,(function(t){return this._invoke(e,t)}))}))}function L(t,e){function r(a,i,o,s){var c=h(t[a],t,i);if("throw"!==c.type){var u=c.arg,l=u.value;return l&&"object"==typeof l&&n.call(l,"__await")?e.resolve(l.__await).then((function(t){r("next",t,o,s)}),(function(t){r("throw",t,o,s)})):e.resolve(l).then((function(t){u.value=t,o(u)}),(function(t){return r("throw",t,o,s)}))}s(c.arg)}var a;this._invoke=function(t,n){function i(){return new e((function(e,a){r(t,n,e,a)}))}return a=a?a.then(i,i):i()}}function P(t,r){var n=t.iterator[r.method];if(n===e){if(r.delegate=null,"throw"===r.method){if(t.iterator.return&&(r.method="return",r.arg=e,P(t,r),"throw"===r.method))return m;r.method="throw",r.arg=new TypeError("The iterator does not provide a 'throw' method")}return m}var a=h(n,t.iterator,r.arg);if("throw"===a.type)return r.method="throw",r.arg=a.arg,r.delegate=null,m;var i=a.arg;return i?i.done?(r[t.resultName]=i.value,r.next=t.nextLoc,"return"!==r.method&&(r.method="next",r.arg=e),r.delegate=null,m):i:(r.method="throw",r.arg=new TypeError("iterator result is not an object"),r.delegate=null,m)}function _(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function O(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function M(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(_,this),this.reset(!0)}function C(t){if(t){var r=t[i];if(r)return r.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var a=-1,o=function r(){for(;++a<t.length;)if(n.call(t,a))return r.value=t[a],r.done=!1,r;return r.value=e,r.done=!0,r};return o.next=o}}return{next:E}}function E(){return{value:e,done:!0}}return g.prototype=v,c(j,"constructor",v),c(v,"constructor",g),g.displayName=c(v,s,"GeneratorFunction"),t.isGeneratorFunction=function(t){var e="function"==typeof t&&t.constructor;return!!e&&(e===g||"GeneratorFunction"===(e.displayName||e.name))},t.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,v):(t.__proto__=v,c(t,s,"GeneratorFunction")),t.prototype=Object.create(j),t},t.awrap=function(t){return{__await:t}},k(L.prototype),c(L.prototype,o,(function(){return this})),t.AsyncIterator=L,t.async=function(e,r,n,a,i){void 0===i&&(i=Promise);var o=new L(u(e,r,n,a),i);return t.isGeneratorFunction(r)?o:o.next().then((function(t){return t.done?t.value:o.next()}))},k(j),c(j,s,"Generator"),c(j,i,(function(){return this})),c(j,"toString",(function(){return"[object Generator]"})),t.keys=function(t){var e=[];for(var r in t)e.push(r);return e.reverse(),function r(){for(;e.length;){var n=e.pop();if(n in t)return r.value=n,r.done=!1,r}return r.done=!0,r}},t.values=C,M.prototype={constructor:M,reset:function(t){if(this.prev=0,this.next=0,this.sent=this._sent=e,this.done=!1,this.delegate=null,this.method="next",this.arg=e,this.tryEntries.forEach(O),!t)for(var r in this)"t"===r.charAt(0)&&n.call(this,r)&&!isNaN(+r.slice(1))&&(this[r]=e)},stop:function(){this.done=!0;var t=this.tryEntries[0].completion;if("throw"===t.type)throw t.arg;return this.rval},dispatchException:function(t){if(this.done)throw t;var r=this;function a(n,a){return s.type="throw",s.arg=t,r.next=n,a&&(r.method="next"Line truncated
|
||||
//# sourceMappingURL=build.js.map
|
||||
Vendored
+1
-1
@@ -1 +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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 truncated
|
||||
{"version":3,"file":"build.js","mappings":"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 truncated
|
||||
@@ -1,573 +0,0 @@
|
||||
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 <DailyArchive.server>)
|
||||
* 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 <addMessage>, 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 };
|
||||
});
|
||||
Generated
+2571
File diff suppressed because it is too large.
Load diff
+14
-12
@@ -1,26 +1,28 @@
|
||||
{
|
||||
"name": "remotestorage-module-chat-messages",
|
||||
"version": "0.7.1",
|
||||
"version": "2.1.0",
|
||||
"description": "Stores chat messages in daily archive files",
|
||||
"main": "./dist/build.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "NODE_ENV=production webpack",
|
||||
"dev": "webpack -w",
|
||||
"build": "NODE_ENV=production webpack"
|
||||
"start": "npm run dev",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"version": "npm run build && git add dist/"
|
||||
},
|
||||
"author": "Kosmos Developers <mail@kosmos.org> (https://kosmos.org)",
|
||||
"contributors": [
|
||||
"Sebastian Kippe <sebastian@kip.pe>"
|
||||
],
|
||||
"author": "Kosmos Contributors <mail@kosmos.org> (https://kosmos.org)",
|
||||
"license": "MIT",
|
||||
"homepage": "https://gitea.kosmos.org/kosmos/rs-module-chat-messages",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/67P/remotestorage-module-chat-messages.git"
|
||||
"url": "https://gitea.kosmos.org/kosmos/rs-module-chat-messages.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.18.2",
|
||||
"babel-loader": "^6.2.7",
|
||||
"babel-preset-es2015": "^6.18.0",
|
||||
"webpack": "^1.13.2"
|
||||
"@babel/core": "^7.14.8",
|
||||
"@babel/preset-env": "^7.14.9",
|
||||
"babel-loader": "^8.2.2",
|
||||
"webpack": "^5.48.0",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"regenerator-runtime": "^0.13.9"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,784 @@
|
||||
import 'regenerator-runtime/runtime';
|
||||
|
||||
function pad (num) {
|
||||
num = String(num);
|
||||
if (num.length === 1) { num = "0" + num; }
|
||||
return num;
|
||||
};
|
||||
|
||||
function parseDate (date) {
|
||||
return {
|
||||
year: date.getUTCFullYear(),
|
||||
month: pad( date.getUTCMonth() + 1 ),
|
||||
day: pad( date.getUTCDate() )
|
||||
};
|
||||
};
|
||||
|
||||
function lowestNumberInListing(items) {
|
||||
const sortedNumbers = Object.keys(items)
|
||||
.map(i => parseInt(i))
|
||||
.filter(i => !Number.isNaN(i))
|
||||
.sort();
|
||||
return sortedNumbers[0];
|
||||
}
|
||||
|
||||
const ChatMessages = function (privateClient, publicClient) {
|
||||
/**
|
||||
* Schema: chat-messages/daily-archive
|
||||
*
|
||||
* Represents one calendar day of chat messages
|
||||
*
|
||||
* @example
|
||||
* {
|
||||
* "@context": "https://kosmos.org/ns/v2",
|
||||
* "@id": "chat-messages/irc.libera.chat/channels/kosmos/",
|
||||
* "@type": "ChatChannel",
|
||||
* "service": {
|
||||
* "domain": "irc.libera.chat",
|
||||
* "protocol": "IRC",
|
||||
* },
|
||||
* "name": "#kosmos",
|
||||
* "type": "room",
|
||||
* "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" }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
const archiveSchema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"@context": {
|
||||
"type": "string",
|
||||
"default": "https://kosmos.org/ns/v2/chat-channel"
|
||||
},
|
||||
"@id": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"@type": {
|
||||
"type": "string",
|
||||
"default": "ChatChannel",
|
||||
"enum": ["ChatChannel"]
|
||||
},
|
||||
"service": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"domain": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"protocol": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"enum": [ "room", "person" ]
|
||||
},
|
||||
"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/v2/chat-channel", archiveSchema);
|
||||
publicClient.declareType("daily-archive", "https://kosmos.org/ns/v2/chat-channel", archiveSchema);
|
||||
|
||||
/**
|
||||
* Schema: chat-messages/daily-archive-meta
|
||||
*
|
||||
* Stores meta information about the daily archives
|
||||
*
|
||||
* @example
|
||||
* {
|
||||
* "@context": "https://kosmos.org/ns/v2",
|
||||
* "@id": "chat-messages/irc.libera.chat/channels/kosmos/meta",
|
||||
* "@type": "ChatChannelMeta",
|
||||
* "first": "2009/01/03",
|
||||
* "last": "2021/11/05"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
const archiveMetaSchema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"@context": {
|
||||
"type": "string",
|
||||
"default": "https://kosmos.org/ns/v2/chat-channel-meta"
|
||||
},
|
||||
"@id": {
|
||||
"type": "string",
|
||||
},
|
||||
"@type": {
|
||||
"type": "string",
|
||||
"default": "ChatChannelMeta",
|
||||
"enum": ["ChatChannelMeta"]
|
||||
},
|
||||
"first": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]{4}\/[0-9]{2}\/[0-9]{2}$"
|
||||
},
|
||||
"last": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]{4}\/[0-9]{2}\/[0-9]{2}$"
|
||||
}
|
||||
},
|
||||
"required": ["@id", "first", "last"]
|
||||
};
|
||||
|
||||
privateClient.declareType("daily-archive-meta", "https://kosmos.org/ns/v2/chat-channel-meta", archiveMetaSchema);
|
||||
publicClient.declareType("daily-archive-meta", "https://kosmos.org/ns/v2/chat-channel-meta", archiveMetaSchema);
|
||||
|
||||
/**
|
||||
* A chat channel
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class Channel {
|
||||
/**
|
||||
* @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.name - Name of room/channel (e.g. "#kosmos")
|
||||
* @param {string} [options.type] - Type of channel ("room" or "person")
|
||||
*
|
||||
* @example
|
||||
* // IRC channel:
|
||||
* const channel = new remoteStorage.chatMessages.Channel({
|
||||
* service: {
|
||||
* protocol: 'IRC',
|
||||
* domain: 'irc.libera.chat',
|
||||
* },
|
||||
* name: '#kosmos-dev',
|
||||
* type: 'room'
|
||||
* });
|
||||
*
|
||||
* // XMPP channel:
|
||||
* const channel = new remoteStorage.chatMessages.Channel({
|
||||
* service: {
|
||||
* protocol: 'XMPP',
|
||||
* domain: 'kosmos.chat',
|
||||
* },
|
||||
* name: 'kosmos-dev',
|
||||
* type: 'room'
|
||||
* });
|
||||
*/
|
||||
constructor (options) {
|
||||
//
|
||||
// Defaults
|
||||
//
|
||||
options.type = options.type || "room";
|
||||
|
||||
//
|
||||
// Validate options
|
||||
//
|
||||
if (typeof options !== "object") {
|
||||
throw "options must be an object";
|
||||
}
|
||||
if (typeof options.service !== "object" ||
|
||||
typeof options.service.protocol !== "string" ||
|
||||
typeof options.service.domain !== "string") {
|
||||
throw "options.service must be an object containing at least service \"protocol\" and \"domain\"";
|
||||
}
|
||||
if (typeof options.name !== "string") {
|
||||
throw "options.name must be a string";
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.service = options.service;
|
||||
|
||||
/**
|
||||
* @property {string} name - Name of channel (e.g. "#kosmos")
|
||||
*/
|
||||
this.name = options.name;
|
||||
|
||||
/**
|
||||
* @property {string} type - Type of channel ("room" or "person")
|
||||
*/
|
||||
this.type = options.type;
|
||||
|
||||
/**
|
||||
* @property {string} path - Base directory path of the channel archives
|
||||
*/
|
||||
if (this.type === "room") {
|
||||
// Normal chatroom
|
||||
const name = this.name.replace(/#/,'');
|
||||
this.path = `${this.service.domain}/channels/${name}`;
|
||||
} else {
|
||||
// User direct messages
|
||||
this.path = `${this.service.domain}/users/${this.name}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @property {string} metaPath - Path of the channel's metadata document
|
||||
*/
|
||||
this.metaPath = `${this.path}/meta`;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the last stored DailyArchive, or a new one for the current day.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {boolean} [options.public] - Public or private archive. Defaults to 'false'.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async openLastDailyArchive (options={}) {
|
||||
const client = options.public ? publicClient : privateClient;
|
||||
const meta = await client.getObject(this.metaPath);
|
||||
|
||||
if (meta && meta.last) {
|
||||
return new DailyArchive({
|
||||
channel: this,
|
||||
date: new Date(meta.last.replace(/\//g,'-')),
|
||||
isPublic: options.public
|
||||
});
|
||||
} else {
|
||||
return new DailyArchive({
|
||||
channel: this,
|
||||
date: new Date(),
|
||||
isPublic: options.public
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a DailyArchive for the given date
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {date} options.date - Date of archive day
|
||||
* @param {boolean} [options.public] - Public or private archive. Defaults to 'false'.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
openDailyArchive (options={}) {
|
||||
if (!(options.date instanceof Date)) {
|
||||
throw 'options.date must be a date object';
|
||||
}
|
||||
|
||||
return new DailyArchive({
|
||||
channel: this,
|
||||
date: options.date,
|
||||
isPublic: options.public
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A daily archive stores chat messages by calendar day.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class DailyArchive {
|
||||
/**
|
||||
* @param {object} options
|
||||
* @param {string} options.channel - A Channel instance
|
||||
* @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
|
||||
* const channel = new remoteStorage.chatMessages.Channel({ ...options })
|
||||
* const archive = new chatMessages.DailyArchive({
|
||||
* channel: channel,
|
||||
* date: new Date(),
|
||||
* isPublic: true
|
||||
* });
|
||||
*/
|
||||
constructor (options) {
|
||||
//
|
||||
// Defaults
|
||||
//
|
||||
options.isPublic = options.isPublic || false;
|
||||
|
||||
//
|
||||
// Validate options
|
||||
//
|
||||
if (typeof options !== "object") {
|
||||
throw "options must be an object";
|
||||
}
|
||||
if (!(options.channel instanceof Channel)) {
|
||||
throw "options.channel must be a Channel object";
|
||||
}
|
||||
if (!(options.date instanceof Date)) {
|
||||
throw "options.date must be a date object";
|
||||
}
|
||||
if (typeof options.isPublic !== "boolean") {
|
||||
throw "options.isPublic must be a boolean value";
|
||||
}
|
||||
|
||||
/**
|
||||
* @property {Channel} channel - A Channel instance
|
||||
*/
|
||||
this.channel = options.channel;
|
||||
|
||||
/**
|
||||
* @property {string} date - Gregorian calendar date of the archive's content
|
||||
*/
|
||||
this.date = options.date;
|
||||
|
||||
/**
|
||||
* @property {boolean} isPublic - `true` for public archives, `false` for private ones
|
||||
*/
|
||||
this.isPublic = options.isPublic || false;
|
||||
|
||||
/**
|
||||
* @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 {string} dateId - Date string in the form of YYYY/MM/DD
|
||||
*/
|
||||
this.dateId = this.parsedDate.year+'/'+this.parsedDate.month+'/'+this.parsedDate.day;
|
||||
|
||||
/**
|
||||
* @property {string} path - Path of the archive document
|
||||
*/
|
||||
this.path = `${this.channel.path}/${this.dateId}`;
|
||||
|
||||
/**
|
||||
* @property {string} metaPath - Path of the channel's metadata document
|
||||
*/
|
||||
this.metaPath = `${this.channel.path}/meta`;
|
||||
|
||||
/**
|
||||
* @property {object} client - Public or private remoteStorgage.js BaseClient
|
||||
*/
|
||||
this.client = this.isPublic ? publicClient : privateClient;
|
||||
|
||||
/**
|
||||
* @property {string} previous - Date of previous log file as YYYY/MM/DD
|
||||
*/
|
||||
this.previous = options.previous;
|
||||
|
||||
/**
|
||||
* @property {string} next - Date of next log file as YYYY/MM/DD
|
||||
*/
|
||||
this.next = options.next;
|
||||
}
|
||||
|
||||
/*
|
||||
* @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.
|
||||
* @param {string} [message.sid] - Unique stanza ID of message (XMPP only)
|
||||
* @param {boolean} [message.edited] - If the message has been edited
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
addMessage (message) {
|
||||
if (this.isPublic && !this.channel.name.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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Like <addMessage>, but for multiple messages at once. Useful for bulk
|
||||
* imports of messages.
|
||||
*
|
||||
* @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.channel.name.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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Deletes the entire archive document from storage
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
remove () {
|
||||
// TODO when removing, if previous is set, but not next, it means the
|
||||
// removed file is the last archive. Thus, set "last" to previous file.
|
||||
return this.client.remove(this.path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates and writes an existing archive document
|
||||
*
|
||||
* @returns {Promise}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
async _updateDocument (archive, messages) {
|
||||
console.debug('[chat-messages] Updating archive document');
|
||||
|
||||
if (Array.isArray(messages)) {
|
||||
messages.forEach(function(message) {
|
||||
archive.today.messages.push(message);
|
||||
});
|
||||
} else {
|
||||
archive.today.messages.push(messages);
|
||||
}
|
||||
|
||||
return this._sync(archive);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates and writes a new archive document
|
||||
*
|
||||
* @returns {Promise}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
async _createDocument (messages) {
|
||||
console.debug('[chat-messages] Creating new archive document');
|
||||
const 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; }
|
||||
} else {
|
||||
// Find and update previous archive, set 'previous' on this one
|
||||
const previous = await this._updatePreviousArchive();
|
||||
if (typeof previous === 'object') {
|
||||
archive.today.previous = previous.today['@id'];
|
||||
}
|
||||
}
|
||||
|
||||
await this._sync(archive);
|
||||
|
||||
// TODO only write meta doc if argument is set on addMessages. This way
|
||||
// we can avoid race conditions when syncing remote chat messages all at
|
||||
// once for multiple days
|
||||
await this._updateArchiveMetaDocument();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Builds the object to be stored in remote storage
|
||||
*
|
||||
* @returns {object}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_buildArchiveObject () {
|
||||
const roomName = this.channel.name.replace(/#/,'');
|
||||
|
||||
const archive = {
|
||||
"@id": "chat-messages/"+this.channel.service.domain+"/channels/"+roomName+"/",
|
||||
"@type": "ChatChannel",
|
||||
"service": this.channel.service,
|
||||
"name": this.channel.name,
|
||||
"type": this.channel.type,
|
||||
"today": {
|
||||
"@id": this.dateId,
|
||||
"@type": "ChatLog",
|
||||
"messageType": "InstantMessage",
|
||||
"messages": []
|
||||
}
|
||||
};
|
||||
|
||||
switch (this.channel.service.protocol) {
|
||||
case 'IRC':
|
||||
if (!this.channel.name.match(/^#/)) {
|
||||
archive["@id"] = "chat-messages/"+this.channel.service.domain+"/users/"+this.channel.name+"/";
|
||||
}
|
||||
break;
|
||||
case 'XMPP':
|
||||
// XMPP-specific adjustments
|
||||
break;
|
||||
}
|
||||
|
||||
return archive;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the previous archive document and updates its today.next value
|
||||
*
|
||||
* @returns {boolean|Promise}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_updatePreviousArchive () {
|
||||
return this._findPreviousArchive().then((archive) => {
|
||||
if (typeof archive === 'object' && archive.today) {
|
||||
archive.today.next = this.dateId;
|
||||
const path = this.path.substring(0, this.path.length-this.dateId.length)+archive.today['@id'];
|
||||
|
||||
return this.client.storeObject('daily-archive', path, archive).then(() => {
|
||||
console.debug('[chat-messages] Previous archive written to remote storage at', path);
|
||||
return archive;
|
||||
});
|
||||
} else {
|
||||
console.debug('[chat-messages] Previous archive not found');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the previous archive document
|
||||
*
|
||||
* @returns {Promise}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_findPreviousArchive () {
|
||||
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) => {
|
||||
const 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) {
|
||||
const 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) => {
|
||||
const 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) {
|
||||
const month = pad(Math.max(...months).toString());
|
||||
|
||||
return this.client.getListing(yearPath+month+'/').then((listing) => {
|
||||
const days = Object.keys(listing).map((i) => parseInt(i));
|
||||
const 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) => {
|
||||
|
||||
const 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) {
|
||||
const year = Math.max(...years).toString();
|
||||
|
||||
return this.client.getListing(basePath+year+'/').then((listing) => {
|
||||
const months = Object.keys(listing).map((i) => parseInt(i.substr(0,2)));
|
||||
const month = pad(Math.max(...months).toString());
|
||||
|
||||
return this.client.getListing(basePath+year+'/'+month+'/').then((listing) => {
|
||||
const days = Object.keys(listing).map((i) => parseInt(i));
|
||||
const day = pad(Math.max(...days).toString());
|
||||
return this.client.getObject(basePath+year+'/'+month+'/'+day);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async _updateArchiveMetaDocument () {
|
||||
const meta = await this.client.getObject(this.metaPath);
|
||||
if (typeof meta !== 'object') {
|
||||
return this._createArchiveMetaDocument();
|
||||
}
|
||||
|
||||
// Only update document if current date is newer than known "last"
|
||||
if (Date.parse(meta.last.replace('/','-')) > Date.parse(this.date)) {
|
||||
console.debug('[chat-messages]', 'Updating meta document for channel');
|
||||
meta.last = this.dateId;
|
||||
await this.client.storeObject('daily-archive-meta', this.metaPath, meta);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
async _createArchiveMetaDocument () {
|
||||
console.debug('[chat-messages]', 'Creating new meta document for channel');
|
||||
// When creating a new meta doc, we need to find the oldest archive,
|
||||
// because older versions of the module did not write a meta doc.
|
||||
const first = await this._findFirstArchive();
|
||||
const roomName = this.channel.name.replace(/#/,'');
|
||||
|
||||
const meta = {
|
||||
'@id': `chat-messages/${this.channel.service.domain}/channels/${roomName}/meta`,
|
||||
'@type': 'ChatChannelMeta',
|
||||
first: first,
|
||||
last: this.dateId // TODO might have to search for last?
|
||||
};
|
||||
|
||||
return this.client.storeObject('daily-archive-meta', this.metaPath, meta)
|
||||
.then(() => console.debug('[chat-messages]', 'Meta document written to remote storage'))
|
||||
.catch(e => {
|
||||
console.log('[chat-messages]', `Failed to store ${this.metaPath}`);
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
async _findFirstArchive () {
|
||||
console.debug('[chat-messages]', 'Finding first archive for channel');
|
||||
const years = await this.client.getListing(`${this.channelPath}/`);
|
||||
const year = lowestNumberInListing(years);
|
||||
const months = await this.client.getListing(`${this.channelPath}/${year}/`);
|
||||
const month = lowestNumberInListing(months);
|
||||
const days = await this.client.getListing(`${this.channelPath}/${year}/${pad(month)}/`);
|
||||
const day = lowestNumberInListing(days);
|
||||
const firstId = `${year}/${pad(month)}/${pad(day)}`;
|
||||
console.debug('[chat-messages]', 'First is', firstId);
|
||||
return firstId;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write archive document
|
||||
*
|
||||
* @returns {Promise}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
async _sync (obj) {
|
||||
console.debug(`[chat-messages] Writing archive object with ${obj.today.messages.length} messages`);
|
||||
|
||||
return this.client.storeObject('daily-archive', this.path, obj).then(function(){
|
||||
console.debug('[chat-messages] Archive written to remote storage');
|
||||
return true;
|
||||
},function(error){
|
||||
console.warn('[chat-messages] Error trying to store object', error);
|
||||
return error;
|
||||
});
|
||||
|
||||
// TODO await this.client.startSync();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
exports: {
|
||||
DailyArchive,
|
||||
privateClient,
|
||||
publicClient
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default { name: 'chat-messages', builder: ChatMessages };
|
||||
+25
-24
@@ -1,32 +1,33 @@
|
||||
var webpack = require('webpack');
|
||||
var isProd = (process.env.NODE_ENV === 'production');
|
||||
|
||||
// minimize only in production
|
||||
var plugins = isProd ? [new webpack.optimize.UglifyJsPlugin({minimize: true })] : []
|
||||
/* global __dirname */
|
||||
const isProd = (process.env.NODE_ENV === 'production');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: './index.js',
|
||||
// source map not in production
|
||||
devtool: !isProd && 'source-map',
|
||||
entry: ['./src/chat-messages.js'],
|
||||
output: {
|
||||
filename: __dirname + '/dist/build.js',
|
||||
libraryTarget: 'umd'
|
||||
},
|
||||
externals: {
|
||||
"remotestoragejs": {
|
||||
root: "RemoteStorage", // <script src='remotestorage.js'> will resolve in this.RemoteStorage
|
||||
commonjs2: "remotestoragejs", // require('remotestoragejs')
|
||||
commonjs: "remotestoragejs", // require('remotestoragejs')
|
||||
amd: "remotestoragejs" // define(['remotestoragejs'], ...)
|
||||
}
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'build.js',
|
||||
library: 'ChatMessages',
|
||||
libraryTarget: 'umd',
|
||||
libraryExport: 'default',
|
||||
umdNamedDefine: true,
|
||||
globalObject: 'this'
|
||||
},
|
||||
mode: isProd ? 'production' : 'development',
|
||||
devtool: isProd ? 'source-map' : 'eval-source-map',
|
||||
module: {
|
||||
loaders: [
|
||||
{ test: /\.js$/, exclude: '/node_modules|dist/', loader: 'babel?presets[]=es2015' },
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['@babel/preset-env']
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['', '.js']
|
||||
},
|
||||
plugins: plugins
|
||||
// plugins: plugins
|
||||
};
|
||||
Reference in new issue
Block a user