2020-08-01 03:43:15 +02:00
|
|
|
"use strict";
|
|
|
|
|
2020-07-28 00:31:25 +02:00
|
|
|
const marked = require("../third/marked/lib/marked");
|
|
|
|
|
2018-06-02 13:59:02 +02:00
|
|
|
/*
|
|
|
|
|
|
|
|
What in the heck is a zcommand?
|
|
|
|
|
|
|
|
A zcommand is basically a specific type of slash
|
|
|
|
command where the client does almost no work and
|
|
|
|
the server just does something pretty simple like
|
|
|
|
flip a setting.
|
|
|
|
|
|
|
|
The first zcommand we wrote is for "/ping", and
|
|
|
|
the server just responds with a 200 for that.
|
|
|
|
|
|
|
|
Not all slash commands use zcommand under the hood.
|
|
|
|
For more exotic things like /poll see submessage.js
|
|
|
|
and widgetize.js
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
exports.send = function (opts) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const command = opts.command;
|
|
|
|
const on_success = opts.on_success;
|
|
|
|
const data = {
|
2020-07-20 22:18:43 +02:00
|
|
|
command,
|
2018-06-02 13:59:02 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
channel.post({
|
2020-07-15 01:29:15 +02:00
|
|
|
url: "/json/zcommand",
|
2020-07-20 22:18:43 +02:00
|
|
|
data,
|
|
|
|
success(data) {
|
2018-12-21 16:45:42 +01:00
|
|
|
if (on_success) {
|
|
|
|
on_success(data);
|
|
|
|
}
|
2018-06-02 13:59:02 +02:00
|
|
|
},
|
2020-07-20 22:18:43 +02:00
|
|
|
error() {
|
2020-07-15 01:29:15 +02:00
|
|
|
exports.tell_user("server did not respond");
|
2018-06-02 13:59:02 +02:00
|
|
|
},
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.tell_user = function (msg) {
|
|
|
|
// This is a bit hacky, but we don't have a super easy API now
|
|
|
|
// for just telling users stuff.
|
2020-07-15 00:34:28 +02:00
|
|
|
$("#compose-send-status")
|
|
|
|
.removeClass(common.status_classes)
|
2020-07-15 01:29:15 +02:00
|
|
|
.addClass("alert-error")
|
2020-07-15 00:34:28 +02:00
|
|
|
.stop(true)
|
|
|
|
.fadeTo(0, 1);
|
2020-07-15 01:29:15 +02:00
|
|
|
$("#compose-error-msg").text(msg);
|
2018-06-02 13:59:02 +02:00
|
|
|
};
|
|
|
|
|
2018-12-21 16:45:42 +01:00
|
|
|
exports.enter_day_mode = function () {
|
2018-06-02 16:23:32 +02:00
|
|
|
exports.send({
|
2018-12-21 16:45:42 +01:00
|
|
|
command: "/day",
|
2020-07-20 22:18:43 +02:00
|
|
|
on_success(data) {
|
2018-12-21 16:45:42 +01:00
|
|
|
night_mode.disable();
|
|
|
|
feedback_widget.show({
|
2020-07-20 22:18:43 +02:00
|
|
|
populate(container) {
|
2020-01-23 08:42:58 +01:00
|
|
|
const rendered_msg = marked(data.msg).trim();
|
|
|
|
container.html(rendered_msg);
|
2018-12-21 16:45:42 +01:00
|
|
|
},
|
2020-07-20 22:18:43 +02:00
|
|
|
on_undo() {
|
2018-12-21 16:45:42 +01:00
|
|
|
exports.send({
|
|
|
|
command: "/night",
|
|
|
|
});
|
|
|
|
},
|
|
|
|
title_text: i18n.t("Day mode"),
|
|
|
|
undo_button_text: i18n.t("Night"),
|
|
|
|
});
|
2018-06-02 16:23:32 +02:00
|
|
|
},
|
|
|
|
});
|
2018-12-21 16:45:42 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
exports.enter_night_mode = function () {
|
|
|
|
exports.send({
|
|
|
|
command: "/night",
|
2020-07-20 22:18:43 +02:00
|
|
|
on_success(data) {
|
2018-12-21 16:45:42 +01:00
|
|
|
night_mode.enable();
|
|
|
|
feedback_widget.show({
|
2020-07-20 22:18:43 +02:00
|
|
|
populate(container) {
|
2020-01-23 08:42:58 +01:00
|
|
|
const rendered_msg = marked(data.msg).trim();
|
|
|
|
container.html(rendered_msg);
|
2018-12-21 16:45:42 +01:00
|
|
|
},
|
2020-07-20 22:18:43 +02:00
|
|
|
on_undo() {
|
2018-12-21 16:45:42 +01:00
|
|
|
exports.send({
|
|
|
|
command: "/day",
|
|
|
|
});
|
|
|
|
},
|
|
|
|
title_text: i18n.t("Night mode"),
|
|
|
|
undo_button_text: i18n.t("Day"),
|
|
|
|
});
|
|
|
|
},
|
|
|
|
});
|
|
|
|
};
|
2018-06-02 16:23:32 +02:00
|
|
|
|
2020-03-12 07:51:47 +01:00
|
|
|
exports.enter_fluid_mode = function () {
|
|
|
|
exports.send({
|
|
|
|
command: "/fluid-width",
|
2020-07-20 22:18:43 +02:00
|
|
|
on_success(data) {
|
2020-03-12 07:51:47 +01:00
|
|
|
scroll_bar.set_layout_width();
|
|
|
|
feedback_widget.show({
|
2020-07-20 22:18:43 +02:00
|
|
|
populate(container) {
|
2020-03-12 07:51:47 +01:00
|
|
|
const rendered_msg = marked(data.msg).trim();
|
|
|
|
container.html(rendered_msg);
|
|
|
|
},
|
2020-07-20 22:18:43 +02:00
|
|
|
on_undo() {
|
2020-03-12 07:51:47 +01:00
|
|
|
exports.send({
|
|
|
|
command: "/fixed-width",
|
|
|
|
});
|
|
|
|
},
|
|
|
|
title_text: i18n.t("Fluid width mode"),
|
|
|
|
undo_button_text: i18n.t("Fixed width"),
|
|
|
|
});
|
|
|
|
},
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.enter_fixed_mode = function () {
|
|
|
|
exports.send({
|
|
|
|
command: "/fixed-width",
|
2020-07-20 22:18:43 +02:00
|
|
|
on_success(data) {
|
2020-03-12 07:51:47 +01:00
|
|
|
scroll_bar.set_layout_width();
|
|
|
|
feedback_widget.show({
|
2020-07-20 22:18:43 +02:00
|
|
|
populate(container) {
|
2020-03-12 07:51:47 +01:00
|
|
|
const rendered_msg = marked(data.msg).trim();
|
|
|
|
container.html(rendered_msg);
|
|
|
|
},
|
2020-07-20 22:18:43 +02:00
|
|
|
on_undo() {
|
2020-03-12 07:51:47 +01:00
|
|
|
exports.send({
|
|
|
|
command: "/fluid-width",
|
|
|
|
});
|
|
|
|
},
|
|
|
|
title_text: i18n.t("Fixed width mode"),
|
|
|
|
undo_button_text: i18n.t("Fluid width"),
|
|
|
|
});
|
|
|
|
},
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2018-06-02 13:59:02 +02:00
|
|
|
exports.process = function (message_content) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const content = message_content.trim();
|
2018-06-02 13:59:02 +02:00
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
if (content === "/ping") {
|
2019-11-02 00:06:25 +01:00
|
|
|
const start_time = new Date();
|
2018-06-02 13:59:02 +02:00
|
|
|
|
|
|
|
exports.send({
|
2018-06-20 20:46:33 +02:00
|
|
|
command: content,
|
2020-07-20 22:18:43 +02:00
|
|
|
on_success() {
|
2019-11-02 00:06:25 +01:00
|
|
|
const end_time = new Date();
|
|
|
|
let diff = end_time - start_time;
|
2018-06-02 13:59:02 +02:00
|
|
|
diff = Math.round(diff);
|
2019-11-02 00:06:25 +01:00
|
|
|
const msg = "ping time: " + diff + "ms";
|
2018-06-02 13:59:02 +02:00
|
|
|
exports.tell_user(msg);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
const day_commands = ["/day", "/light"];
|
js: Convert a.indexOf(…) !== -1 to a.includes(…).
Babel polyfills this for us for Internet Explorer.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import K from "ast-types/gen/kinds";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
recast.visit(ast, {
visitBinaryExpression(path) {
const { operator, left, right } = path.node;
if (
n.CallExpression.check(left) &&
n.MemberExpression.check(left.callee) &&
!left.callee.computed &&
n.Identifier.check(left.callee.property) &&
left.callee.property.name === "indexOf" &&
left.arguments.length === 1 &&
checkExpression(left.arguments[0]) &&
((["===", "!==", "==", "!=", ">", "<="].includes(operator) &&
n.UnaryExpression.check(right) &&
right.operator == "-" &&
n.Literal.check(right.argument) &&
right.argument.value === 1) ||
([">=", "<"].includes(operator) &&
n.Literal.check(right) &&
right.value === 0))
) {
const test = b.callExpression(
b.memberExpression(left.callee.object, b.identifier("includes")),
[left.arguments[0]]
);
path.replace(
["!==", "!=", ">", ">="].includes(operator)
? test
: b.unaryExpression("!", test)
);
changed = true;
}
this.traverse(path);
},
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-08 04:55:06 +01:00
|
|
|
if (day_commands.includes(content)) {
|
2018-12-21 16:45:42 +01:00
|
|
|
exports.enter_day_mode();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
const night_commands = ["/night", "/dark"];
|
js: Convert a.indexOf(…) !== -1 to a.includes(…).
Babel polyfills this for us for Internet Explorer.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import K from "ast-types/gen/kinds";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
recast.visit(ast, {
visitBinaryExpression(path) {
const { operator, left, right } = path.node;
if (
n.CallExpression.check(left) &&
n.MemberExpression.check(left.callee) &&
!left.callee.computed &&
n.Identifier.check(left.callee.property) &&
left.callee.property.name === "indexOf" &&
left.arguments.length === 1 &&
checkExpression(left.arguments[0]) &&
((["===", "!==", "==", "!=", ">", "<="].includes(operator) &&
n.UnaryExpression.check(right) &&
right.operator == "-" &&
n.Literal.check(right.argument) &&
right.argument.value === 1) ||
([">=", "<"].includes(operator) &&
n.Literal.check(right) &&
right.value === 0))
) {
const test = b.callExpression(
b.memberExpression(left.callee.object, b.identifier("includes")),
[left.arguments[0]]
);
path.replace(
["!==", "!=", ">", ">="].includes(operator)
? test
: b.unaryExpression("!", test)
);
changed = true;
}
this.traverse(path);
},
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-08 04:55:06 +01:00
|
|
|
if (night_commands.includes(content)) {
|
2018-12-21 16:45:42 +01:00
|
|
|
exports.enter_night_mode();
|
2018-06-02 16:23:32 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
if (content === "/fluid-width") {
|
2020-03-12 07:51:47 +01:00
|
|
|
exports.enter_fluid_mode();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
if (content === "/fixed-width") {
|
2020-03-12 07:51:47 +01:00
|
|
|
exports.enter_fixed_mode();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
if (content === "/settings") {
|
|
|
|
hashchange.go_to_location("settings/your-account");
|
2018-06-17 21:10:57 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-06-02 13:59:02 +02:00
|
|
|
// It is incredibly important here to return false
|
|
|
|
// if we don't see an actual zcommand, so that compose.js
|
|
|
|
// knows this is a normal message.
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2019-10-25 09:45:13 +02:00
|
|
|
window.zcommand = exports;
|