mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-05-29 09:21:16 +03:00
Though the dictionary format works fine, it's slow. Access times are around the 1ms range for rooms like HQ, which doesn't seem like much, but when compared to the Map's access time of 0.05ms it's slow. Converting things to a map means we lose index access and have to instead use `.keys()` and `.values()`, both of which return iterables and not arrays. Even doing the `Array.from()` conversion we see times in the 0.05ms range. This is also what makes this a breaking change. Memory-wise there does not appear to be any measurable impact for a large account.
410 lines
12 KiB
JavaScript
410 lines
12 KiB
JavaScript
var myUserId = "@example:localhost";
|
|
var myAccessToken = "QGV4YW1wbGU6bG9jYWxob3N0.qPEvLuYfNBjxikiCjP";
|
|
var sdk = require("matrix-js-sdk");
|
|
var clc = require("cli-color");
|
|
var matrixClient = sdk.createClient({
|
|
baseUrl: "http://localhost:8008",
|
|
accessToken: myAccessToken,
|
|
userId: myUserId
|
|
});
|
|
|
|
// Data structures
|
|
var roomList = [];
|
|
var viewingRoom = null;
|
|
var numMessagesToShow = 20;
|
|
|
|
// Reading from stdin
|
|
var CLEAR_CONSOLE = '\x1B[2J';
|
|
var readline = require("readline");
|
|
var rl = readline.createInterface({
|
|
input: process.stdin,
|
|
output: process.stdout,
|
|
completer: completer
|
|
});
|
|
rl.setPrompt("$ ");
|
|
rl.on('line', function(line) {
|
|
if (line.trim().length === 0) {
|
|
rl.prompt();
|
|
return;
|
|
}
|
|
if (line === "/help") {
|
|
printHelp();
|
|
rl.prompt();
|
|
return;
|
|
}
|
|
|
|
if (viewingRoom) {
|
|
if (line === "/exit") {
|
|
viewingRoom = null;
|
|
printRoomList();
|
|
}
|
|
else if (line === "/members") {
|
|
printMemberList(viewingRoom);
|
|
}
|
|
else if (line === "/roominfo") {
|
|
printRoomInfo(viewingRoom);
|
|
}
|
|
else if (line === "/resend") {
|
|
// get the oldest not sent event.
|
|
var notSentEvent;
|
|
for (var i = 0; i < viewingRoom.timeline.length; i++) {
|
|
if (viewingRoom.timeline[i].status == sdk.EventStatus.NOT_SENT) {
|
|
notSentEvent = viewingRoom.timeline[i];
|
|
break;
|
|
}
|
|
}
|
|
if (notSentEvent) {
|
|
matrixClient.resendEvent(notSentEvent, viewingRoom).then(function() {
|
|
printMessages();
|
|
rl.prompt();
|
|
}, function(err) {
|
|
printMessages();
|
|
print("/resend Error: %s", err);
|
|
rl.prompt();
|
|
});
|
|
printMessages();
|
|
rl.prompt();
|
|
}
|
|
}
|
|
else if (line.indexOf("/more ") === 0) {
|
|
var amount = parseInt(line.split(" ")[1]) || 20;
|
|
matrixClient.scrollback(viewingRoom, amount).then(function(room) {
|
|
printMessages();
|
|
rl.prompt();
|
|
}, function(err) {
|
|
print("/more Error: %s", err);
|
|
});
|
|
}
|
|
else if (line.indexOf("/invite ") === 0) {
|
|
var userId = line.split(" ")[1].trim();
|
|
matrixClient.invite(viewingRoom.roomId, userId).then(function() {
|
|
printMessages();
|
|
rl.prompt();
|
|
}, function(err) {
|
|
print("/invite Error: %s", err);
|
|
});
|
|
}
|
|
else if (line.indexOf("/file ") === 0) {
|
|
var filename = line.split(" ")[1].trim();
|
|
var stream = fs.createReadStream(filename);
|
|
matrixClient.uploadContent({
|
|
stream: stream,
|
|
name: filename
|
|
}).then(function(url) {
|
|
var content = {
|
|
msgtype: "m.file",
|
|
body: filename,
|
|
url: JSON.parse(url).content_uri
|
|
};
|
|
matrixClient.sendMessage(viewingRoom.roomId, content);
|
|
});
|
|
}
|
|
else {
|
|
matrixClient.sendTextMessage(viewingRoom.roomId, line).finally(function() {
|
|
printMessages();
|
|
rl.prompt();
|
|
});
|
|
// print local echo immediately
|
|
printMessages();
|
|
}
|
|
}
|
|
else {
|
|
if (line.indexOf("/join ") === 0) {
|
|
var roomIndex = line.split(" ")[1];
|
|
viewingRoom = roomList[roomIndex];
|
|
if (viewingRoom.getMember(myUserId).membership === "invite") {
|
|
// join the room first
|
|
matrixClient.joinRoom(viewingRoom.roomId).then(function(room) {
|
|
setRoomList();
|
|
viewingRoom = room;
|
|
printMessages();
|
|
rl.prompt();
|
|
}, function(err) {
|
|
print("/join Error: %s", err);
|
|
});
|
|
}
|
|
else {
|
|
printMessages();
|
|
}
|
|
}
|
|
}
|
|
rl.prompt();
|
|
});
|
|
// ==== END User input
|
|
|
|
// show the room list after syncing.
|
|
matrixClient.on("sync", function(state, prevState, data) {
|
|
switch (state) {
|
|
case "PREPARED":
|
|
setRoomList();
|
|
printRoomList();
|
|
printHelp();
|
|
rl.prompt();
|
|
break;
|
|
}
|
|
});
|
|
|
|
matrixClient.on("Room", function() {
|
|
setRoomList();
|
|
if (!viewingRoom) {
|
|
printRoomList();
|
|
rl.prompt();
|
|
}
|
|
});
|
|
|
|
// print incoming messages.
|
|
matrixClient.on("Room.timeline", function(event, room, toStartOfTimeline) {
|
|
if (toStartOfTimeline) {
|
|
return; // don't print paginated results
|
|
}
|
|
if (!viewingRoom || viewingRoom.roomId !== room.roomId) {
|
|
return; // not viewing a room or viewing the wrong room.
|
|
}
|
|
printLine(event);
|
|
});
|
|
|
|
function setRoomList() {
|
|
roomList = matrixClient.getRooms();
|
|
roomList.sort(function(a,b) {
|
|
// < 0 = a comes first (lower index) - we want high indexes = newer
|
|
var aMsg = a.timeline[a.timeline.length-1];
|
|
if (!aMsg) {
|
|
return -1;
|
|
}
|
|
var bMsg = b.timeline[b.timeline.length-1];
|
|
if (!bMsg) {
|
|
return 1;
|
|
}
|
|
if (aMsg.getTs() > bMsg.getTs()) {
|
|
return 1;
|
|
}
|
|
else if (aMsg.getTs() < bMsg.getTs()) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
function printRoomList() {
|
|
print(CLEAR_CONSOLE);
|
|
print("Room List:");
|
|
var fmts = {
|
|
"invite": clc.cyanBright,
|
|
"leave": clc.blackBright
|
|
};
|
|
for (var i = 0; i < roomList.length; i++) {
|
|
var msg = roomList[i].timeline[roomList[i].timeline.length-1];
|
|
var dateStr = "---";
|
|
var fmt;
|
|
if (msg) {
|
|
dateStr = new Date(msg.getTs()).toISOString().replace(
|
|
/T/, ' ').replace(/\..+/, '');
|
|
}
|
|
var myMembership = roomList[i].getMyMembership();
|
|
if (myMembership) {
|
|
fmt = fmts[myMembership];
|
|
}
|
|
var roomName = fixWidth(roomList[i].name, 25);
|
|
print(
|
|
"[%s] %s (%s members) %s",
|
|
i, fmt ? fmt(roomName) : roomName,
|
|
roomList[i].getJoinedMembers().length,
|
|
dateStr
|
|
);
|
|
}
|
|
}
|
|
|
|
function printHelp() {
|
|
var hlp = clc.italic.white;
|
|
print("Global commands:", hlp);
|
|
print(" '/help' : Show this help.", clc.white);
|
|
print("Room list index commands:", hlp);
|
|
print(" '/join <index>' Join a room, e.g. '/join 5'", clc.white);
|
|
print("Room commands:", hlp);
|
|
print(" '/exit' Return to the room list index.", clc.white);
|
|
print(" '/members' Show the room member list.", clc.white);
|
|
print(" '/invite @foo:bar' Invite @foo:bar to the room.", clc.white);
|
|
print(" '/more 15' Scrollback 15 events", clc.white);
|
|
print(" '/resend' Resend the oldest event which failed to send.", clc.white);
|
|
print(" '/roominfo' Display room info e.g. name, topic.", clc.white);
|
|
}
|
|
|
|
function completer(line) {
|
|
var completions = [
|
|
"/help", "/join ", "/exit", "/members", "/more ", "/resend", "/invite"
|
|
];
|
|
var hits = completions.filter(function(c) { return c.indexOf(line) == 0 });
|
|
// show all completions if none found
|
|
return [hits.length ? hits : completions, line]
|
|
}
|
|
|
|
function printMessages() {
|
|
if (!viewingRoom) {
|
|
printRoomList();
|
|
return;
|
|
}
|
|
print(CLEAR_CONSOLE);
|
|
var mostRecentMessages = viewingRoom.timeline;
|
|
for (var i = 0; i < mostRecentMessages.length; i++) {
|
|
printLine(mostRecentMessages[i]);
|
|
}
|
|
}
|
|
|
|
function printMemberList(room) {
|
|
var fmts = {
|
|
"join": clc.green,
|
|
"ban": clc.red,
|
|
"invite": clc.blue,
|
|
"leave": clc.blackBright
|
|
};
|
|
var members = room.currentState.getMembers();
|
|
// sorted based on name.
|
|
members.sort(function(a, b) {
|
|
if (a.name > b.name) {
|
|
return -1;
|
|
}
|
|
if (a.name < b.name) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
});
|
|
print("Membership list for room \"%s\"", room.name);
|
|
print(new Array(room.name.length + 28).join("-"));
|
|
room.currentState.getMembers().forEach(function(member) {
|
|
if (!member.membership) {
|
|
return;
|
|
}
|
|
var fmt = fmts[member.membership] || function(a){return a;};
|
|
var membershipWithPadding = (
|
|
member.membership + new Array(10 - member.membership.length).join(" ")
|
|
);
|
|
print(
|
|
"%s"+fmt(" :: ")+"%s"+fmt(" (")+"%s"+fmt(")"),
|
|
membershipWithPadding, member.name,
|
|
(member.userId === myUserId ? "Me" : member.userId),
|
|
fmt
|
|
);
|
|
});
|
|
}
|
|
|
|
function printRoomInfo(room) {
|
|
var eventMap = room.currentState.events;
|
|
var eTypeHeader = " Event Type(state_key) ";
|
|
var sendHeader = " Sender ";
|
|
// pad content to 100
|
|
var restCount = (
|
|
100 - "Content".length - " | ".length - " | ".length -
|
|
eTypeHeader.length - sendHeader.length
|
|
);
|
|
var padSide = new Array(Math.floor(restCount/2)).join(" ");
|
|
var contentHeader = padSide + "Content" + padSide;
|
|
print(eTypeHeader+sendHeader+contentHeader);
|
|
print(new Array(100).join("-"));
|
|
eventMap.keys().forEach(function(eventType) {
|
|
if (eventType === "m.room.member") { return; } // use /members instead.
|
|
var eventEventMap = eventMap.get(eventType);
|
|
eventEventMap.keys().forEach(function(stateKey) {
|
|
var typeAndKey = eventType + (
|
|
stateKey.length > 0 ? "("+stateKey+")" : ""
|
|
);
|
|
var typeStr = fixWidth(typeAndKey, eTypeHeader.length);
|
|
var event = eventEventMap.get(stateKey);
|
|
var sendStr = fixWidth(event.getSender(), sendHeader.length);
|
|
var contentStr = fixWidth(
|
|
JSON.stringify(event.getContent()), contentHeader.length
|
|
);
|
|
print(typeStr+" | "+sendStr+" | "+contentStr);
|
|
});
|
|
})
|
|
}
|
|
|
|
function printLine(event) {
|
|
var fmt;
|
|
var name = event.sender ? event.sender.name : event.getSender();
|
|
var time = new Date(
|
|
event.getTs()
|
|
).toISOString().replace(/T/, ' ').replace(/\..+/, '');
|
|
var separator = "<<<";
|
|
if (event.getSender() === myUserId) {
|
|
name = "Me";
|
|
separator = ">>>";
|
|
if (event.status === sdk.EventStatus.SENDING) {
|
|
separator = "...";
|
|
fmt = clc.xterm(8);
|
|
}
|
|
else if (event.status === sdk.EventStatus.NOT_SENT) {
|
|
separator = " x ";
|
|
fmt = clc.redBright;
|
|
}
|
|
}
|
|
var body = "";
|
|
|
|
var maxNameWidth = 15;
|
|
if (name.length > maxNameWidth) {
|
|
name = name.substr(0, maxNameWidth-1) + "\u2026";
|
|
}
|
|
|
|
if (event.getType() === "m.room.message") {
|
|
body = event.getContent().body;
|
|
}
|
|
else if (event.isState()) {
|
|
var stateName = event.getType();
|
|
if (event.getStateKey().length > 0) {
|
|
stateName += " ("+event.getStateKey()+")";
|
|
}
|
|
body = (
|
|
"[State: "+stateName+" updated to: "+JSON.stringify(event.getContent())+"]"
|
|
);
|
|
separator = "---";
|
|
fmt = clc.xterm(249).italic;
|
|
}
|
|
else {
|
|
// random message event
|
|
body = (
|
|
"[Message: "+event.getType()+" Content: "+JSON.stringify(event.getContent())+"]"
|
|
);
|
|
separator = "---";
|
|
fmt = clc.xterm(249).italic;
|
|
}
|
|
if (fmt) {
|
|
print(
|
|
"[%s] %s %s %s", time, name, separator, body, fmt
|
|
);
|
|
}
|
|
else {
|
|
print("[%s] %s %s %s", time, name, separator, body);
|
|
}
|
|
}
|
|
|
|
function print(str, formatter) {
|
|
if (typeof arguments[arguments.length-1] === "function") {
|
|
// last arg is the formatter so get rid of it and use it on each
|
|
// param passed in but not the template string.
|
|
var newArgs = [];
|
|
var i = 0;
|
|
for (i=0; i<arguments.length-1; i++) {
|
|
newArgs.push(arguments[i]);
|
|
}
|
|
var fmt = arguments[arguments.length-1];
|
|
for (i=0; i<newArgs.length; i++) {
|
|
newArgs[i] = fmt(newArgs[i]);
|
|
}
|
|
console.log.apply(console.log, newArgs);
|
|
}
|
|
else {
|
|
console.log.apply(console.log, arguments);
|
|
}
|
|
}
|
|
|
|
function fixWidth(str, len) {
|
|
if (str.length > len) {
|
|
return str.substr(0, len-2) + "\u2026";
|
|
}
|
|
else if (str.length < len) {
|
|
return str + new Array(len - str.length).join(" ");
|
|
}
|
|
return str;
|
|
}
|
|
|
|
matrixClient.startClient(numMessagesToShow); // messages for each room.
|