From b4eb455295ce8fb229410d076631d4be4c5a11b7 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 25 Nov 2017 18:32:32 +0200 Subject: [PATCH] Add jsdocs and jsdoc generator (ref #1) --- .gitignore | 1 + package-lock.json | 103 ++++++++++++++++++++++++++++++++++++++--- package.json | 6 ++- scripts/gen-jsdoc | 7 +++ src/app.js | 70 +++++++++++++++++++++++----- src/commands.js | 16 +++++++ src/portal.js | 3 ++ src/telegram-peer.js | 54 ++++++++++++++++++++- src/telegram-puppet.js | 3 ++ 9 files changed, 240 insertions(+), 23 deletions(-) create mode 100755 scripts/gen-jsdoc diff --git a/.gitignore b/.gitignore index ebebc999..76a76c87 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules/ .idea/ +jsdoc/ config.yaml registration.yaml *.db diff --git a/package-lock.json b/package-lock.json index c8065473..9e0815a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -259,6 +259,11 @@ } } }, + "babylon": { + "version": "7.0.0-beta.19", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz", + "integrity": "sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A==" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -597,6 +602,14 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.9.0.tgz", "integrity": "sha1-t7Zc5r8UE4hlOc/VM/CzDv+pz4g=" }, + "catharsis": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz", + "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=", + "requires": { + "underscore-contrib": "0.3.0" + } + }, "chalk": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", @@ -702,11 +715,6 @@ "delayed-stream": "0.0.5" } }, - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" - }, "commondir": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-0.0.1.tgz", @@ -2245,6 +2253,14 @@ "esprima": "4.0.0" } }, + "js2xmlparser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz", + "integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=", + "requires": { + "xmlcreate": "1.0.2" + } + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -2257,6 +2273,32 @@ "integrity": "sha512-xYuhvQ7I9PDJIGBWev9xm0+SMSed3ZDBAmvVjbFR1ZRLAF+vlXcQu6cRI9uAlj81rzikElRVteehwV7DuX2ZmQ==", "dev": true }, + "jsdoc": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz", + "integrity": "sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg==", + "requires": { + "babylon": "7.0.0-beta.19", + "bluebird": "3.5.1", + "catharsis": "0.8.9", + "escape-string-regexp": "1.0.5", + "js2xmlparser": "3.0.0", + "klaw": "2.0.0", + "marked": "0.3.6", + "mkdirp": "0.5.1", + "requizzle": "0.2.1", + "strip-json-comments": "2.0.1", + "taffydb": "2.6.2", + "underscore": "1.8.3" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -2347,6 +2389,14 @@ } } }, + "klaw": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz", + "integrity": "sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=", + "requires": { + "graceful-fs": "4.1.11" + } + }, "labeled-stream-splicer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-1.0.2.tgz", @@ -3581,6 +3631,21 @@ "resolve-from": "1.0.1" } }, + "requizzle": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", + "integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=", + "requires": { + "underscore": "1.6.0" + }, + "dependencies": { + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + } + } + }, "resolve": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", @@ -4023,8 +4088,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "subarg": { "version": "1.0.0", @@ -4067,6 +4131,11 @@ "string-width": "2.1.1" } }, + "taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=" + }, "telegram-mtproto": { "version": "3.2.11", "resolved": "https://registry.npmjs.org/telegram-mtproto/-/telegram-mtproto-3.2.11.tgz", @@ -4227,6 +4296,21 @@ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=" }, + "underscore-contrib": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz", + "integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=", + "requires": { + "underscore": "1.6.0" + }, + "dependencies": { + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + } + } + }, "universalify": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", @@ -4368,6 +4452,11 @@ "mkdirp": "0.5.1" } }, + "xmlcreate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz", + "integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8=" + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", diff --git a/package.json b/package.json index 8d62f59c..4c00fa0a 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,11 @@ "devDependencies": { "eslint": "4.11.x", "eslint-config-airbnb-base": "12.1.x", - "eslint-plugin-import": "2.8.x" + "eslint-plugin-import": "2.8.x", + "jsdoc": "3.5.x" }, "scripts": { - "fix-auth-renewal": "./scripts/fix-auth-renewal" + "fix-auth-renewal": "./scripts/fix-auth-renewal", + "gen-jsdoc": "./scripts/gen-jsdoc" } } diff --git a/scripts/gen-jsdoc b/scripts/gen-jsdoc new file mode 100755 index 00000000..dfb01097 --- /dev/null +++ b/scripts/gen-jsdoc @@ -0,0 +1,7 @@ +#!/bin/bash +jsdoc src/ \ + --private \ + --recurse \ + --package package.json \ + --readme README.md \ + --destination jsdoc diff --git a/src/app.js b/src/app.js index 3559056c..d545b3a2 100644 --- a/src/app.js +++ b/src/app.js @@ -34,13 +34,42 @@ class MautrixTelegram { constructor(config) { this.config = config + /** + * MXID -> {@link MatrixUser} cache. + * @private + * @type {Map} + */ this.matrixUsersByID = new Map() + /** + * Telegram ID -> {@link TelegramUser} cache. + * @private + * @type {Map} + */ this.telegramUsersByID = new Map() + /** + * Telegram peer ID -> {@link Portal} cache. + * @private + * @type {Map} + */ this.portalsByPeerID = new Map() + /** + * Matrix room ID -> {@link Portal} cache. + * @private + * @type {Map} + */ this.portalsByRoomID = new Map() + /** + * List of management rooms. + * @type {Array} + */ this.managementRooms = [] const self = this + /** + * The matrix-appservice-bridge Bridge instance. + * @private + * @type {Bridge} + */ this.bridge = new Bridge({ homeserverUrl: config.homeserver.address, domain: config.homeserver.domain, @@ -95,17 +124,29 @@ class MautrixTelegram { * This does not care if a {@link TelegramUser} object for the user ID exists. * It simply returns an intent for a Matrix puppet user with the correct MXID. * - * @param {number} id The ID of the Telegram user. - * @returns {Intent} The Matrix puppet intent for the given Telegram user. + * @param {number} id The ID of the Telegram user. + * @returns {Intent} The Matrix puppet intent for the given Telegram user. */ getIntentForTelegramUser(id) { return this.bridge.getIntentFromLocalpart(this.getUsernameForTelegramUser(id)) } + /** + * Get the Matrix username localpart for the Telegram user with the given ID. + * + * @param {number} id The ID of the Telegram user. + * @returns {string} The Matrix username localpart for the given Telegram user. + */ getUsernameForTelegramUser(id) { return this.config.bridge.username_template.replace("${ID}", id) } + /** + * Get the matrix.to link for the Matrix puppet of the Telegram user with the given ID. + * + * @param {number} id The ID of the Telegram user. + * @returns {string} A matrix.to link that points to the Matrix puppet of the given user. + */ getMatrixToLinkForTelegramUser(id) { return `https://matrix.to/#/@${this.getUsernameForTelegramUser(id)}:${this.config.homeserver.domain}` } @@ -116,8 +157,8 @@ class MautrixTelegram { * This will either get the room from the room cache or the bridge room database. * If the room is not found, a new {@link Portal} object is created. * - * @param {TelegramPeer} peer The TelegramPeer object whose portal to get. - * @returns {Promise} The Portal object. + * @param {TelegramPeer} peer The TelegramPeer object whose portal to get. + * @returns {Portal} The Portal object. */ async getPortalByPeer(peer, { createIfNotFound = true } = {}) { let portal = this.portalsByPeerID.get(peer.id) @@ -160,10 +201,10 @@ class MautrixTelegram { * * This will either get the room from the room cache or the bridge room database. * If the room is not found, this function WILL NOT create a new room, - * but rather just return {@linkplain undefined}. + * but rather just return {@code undefined}. * - * @param {string} id The Matrix room ID of the portal to get. - * @returns {Promise} The Portal object. + * @param {string} id The Matrix room ID of the portal to get. + * @returns {Portal} The Portal object. */ async getPortalByRoomID(id) { let portal = this.portalsByRoomID.get(id) @@ -209,8 +250,8 @@ class MautrixTelegram { * This will either get the user from the user cache or the bridge user database. * If the user is not found, a new {@link TelegramUser} instance is created. * - * @param {number} id The internal Telegram ID of the user to get. - * @returns {Promise} The TelegramUser object. + * @param {number} id The internal Telegram ID of the user to get. + * @returns {TelegramUser} The TelegramUser object. */ async getTelegramUser(id, { createIfNotFound = true } = {}) { let user = this.telegramUsersByID.get(id) @@ -246,8 +287,8 @@ class MautrixTelegram { * This will either get the user from the user cache or the bridge user database. * If the user is not found, a new {@link MatrixUser} instance is created. * - * @param {string} id The MXID of the Matrix user to get. - * @returns {Promise} The MatrixUser object. + * @param {string} id The MXID of the Matrix user to get. + * @returns {MatrixUser} The MatrixUser object. */ async getMatrixUser(id, { createIfNotFound = true } = {}) { let user = this.matrixUsersByID.get(id) @@ -305,6 +346,11 @@ class MautrixTelegram { }, entry) } + /** + * Get the members in the given room. + * @param {string} roomID The ID of the room to search. + * @returns {Array} The list of MXIDs who are in the room. + */ async getRoomMembers(roomID) { const roomState = await this.botIntent.roomState(roomID) const members = [] @@ -319,7 +365,7 @@ class MautrixTelegram { /** * Handle a single received Matrix event. * - * @param evt The Matrix event that occurred. + * @param {MatrixEvent} evt The Matrix event that occurred. */ async handleMatrixEvent(evt) { const user = await this.getMatrixUser(evt.sender) diff --git a/src/commands.js b/src/commands.js index 934aaa0b..86ed1b9f 100644 --- a/src/commands.js +++ b/src/commands.js @@ -17,6 +17,22 @@ const makePasswordHash = require("telegram-mtproto").plugins.makePasswordHash const commands = {} +/** + * Module containing all management commands. + * + * @module commands + */ + +/** + * Run management command. + * + * @param {string} sender The MXID of the user who sent the command. + * @param {string} command The command itself. + * @param {Array} args A list of arguments. + * @param {function} reply A function that is called to reply to the command. + * @param {MautrixTelegram} app The MautrixTelegram instance. + * @param {MatrixEvent} evt The event that caused this call. + */ function run(sender, command, args, reply, app, evt) { const commandFunc = this.commands[command] if (!commandFunc) { diff --git a/src/portal.js b/src/portal.js index bb67d14c..e0a93421 100644 --- a/src/portal.js +++ b/src/portal.js @@ -15,6 +15,9 @@ // along with this program. If not, see . const TelegramPeer = require("./telegram-peer") +/** + * Portal represents a portal from a Matrix room to a Telegram chat. + */ class Portal { constructor(app, roomID, peer) { this.app = app diff --git a/src/telegram-peer.js b/src/telegram-peer.js index 690138ed..8a860a3e 100644 --- a/src/telegram-peer.js +++ b/src/telegram-peer.js @@ -14,6 +14,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +/** + * TelegramPeer represents some Telegram entity that can be messaged. + * + * The possible peer types are chat (groups), channel (includes supergroups) and user. + */ class TelegramPeer { constructor(type, id, { accessHash, receiverID, username, title } = {}) { this.type = type @@ -24,6 +29,14 @@ class TelegramPeer { this.title = title } + /** + * Create a TelegramPeer based on peer data received from Telegram. + * + * @param {Object} peer The data received from Telegram. + * @param {number} sender The user ID of the other person, in case the peer is an user referring to the receiver. + * @param {number} receiverID The user ID of the receiver (in case peer type is {@code user}) + * @returns {TelegramPeer} + */ static fromTelegramData(peer, sender, receiverID) { switch (peer._) { case "peerChat": @@ -48,9 +61,9 @@ class TelegramPeer { * @param {MautrixTelegram} app The instance of {@link MautrixTelegram} to use. * @param {TelegramPuppet} telegramPOV The puppeted Telegram user for whom the access hash is needed. * @param {Portal} [portal] Optional channel {@link Portal} instance to avoid calling {@link app#getPortalByPeer(peer)}. - * Only used if {@link #type} is {@linkplain user}. + * Only used if {@link #type} is {@code user}. * @param {TelegramUser} [user] Optional {@link TelegramUser} instance to avoid calling {@link app#getTelegramUser(id)}. - * Only used if {@link #type} is {@linkplain channel}. + * Only used if {@link #type} is {@code channel}. * @returns {Promise} Whether or not the access hash was found and loaded. */ async loadAccessHash(app, telegramPOV, { portal, user } = {}) { @@ -74,6 +87,12 @@ class TelegramPeer { return false } + /** + * Update info based on a Telegram dialog. + * + * @param dialog The dialog data sent by Telegram. + * @returns {boolean} Whether or not something was changed. + */ async updateInfo(dialog) { let changed = false if (this.type === "channel" || this.type === "user") { @@ -89,6 +108,13 @@ class TelegramPeer { return changed } + /** + * Get info about this peer from the Telegram servers. + * + * @param {TelegramPuppet} telegramPOV The Telegram user whose point of view the data should be fetched from. + * @returns {{info: Object, users: Array}} The info sent by Telegram. For user-type peers, the users array + * is unnecessary. + */ async getInfo(telegramPOV) { let info, users switch (this.type) { @@ -128,6 +154,11 @@ class TelegramPeer { } } + /** + * Create a Telegram InputPeer object based on the data in this TelegramPeer. + * + * @returns {Object} The Telegram InputPeer object. + */ toInputPeer() { switch (this.type) { case "chat": @@ -152,8 +183,15 @@ class TelegramPeer { } } + /** + * Create a Telegram input* object (i.e. inputUser or inputChannel) based on the data in this TelegramPeer. + * + * @returns {Object} The Telegram input* object. + */ toInputObject() { switch (this.type) { + case "chat": + throw new Error(`Unsupported type ${this.type}`) case "user": return { _: "inputUser", @@ -171,10 +209,21 @@ class TelegramPeer { } } + /** + * Load the data in a database subentry to a new TelegramPeer object. + * + * @param {Object} entry The database subentry. + * @returns {TelegramPeer} The created TelegramPeer object. + */ static fromSubentry(entry) { return new TelegramPeer(entry.type, entry.id, entry) } + /** + * Convert this TelegramPeer into a subentry that can be stored in the database. + * + * @returns {Object} The database-storable subentry. + */ toSubentry() { return { type: this.type, @@ -185,6 +234,7 @@ class TelegramPeer { } } + // TODO determine if this is useless and remove if it is. get key() { return `${this.type} ${this.id}` } diff --git a/src/telegram-puppet.js b/src/telegram-puppet.js index 96e2689e..1122aa49 100644 --- a/src/telegram-puppet.js +++ b/src/telegram-puppet.js @@ -17,6 +17,9 @@ const telegram = require("telegram-mtproto") const pkg = require("../package.json") const TelegramPeer = require("./telegram-peer") +/* + * Mapping from Telegram file types to MIME types and extensions. + */ const META_FROM_FILETYPE = { "storage.fileGif": { mimetype: "image/gif",