Git Product home page Git Product logo

rocket.chat.apps-engine's Introduction

Thoughts While Working (for docs)

  • Apps which don't provide a valid uuid4 id will be assigned one, but this is not recommended and your App should provide an id
  • The language strings are only done on the clients (TAPi18next.addResourceBundle(lang, projectName, translations);)
  • The implementer of this should restrict the server setting access and environmental variables. Idea is to allow the implementer to have a default set of restricted ones while letting the admin/owner of the server to restrict it even further or lift the restriction on some more. Simple interface with settings and checkbox to allow/disallow them. πŸ€”

What does the Apps-Engine enable you to do?

The Apps-Engine is Rocket.Chat's plugin framework - it provides the APIs for Rocket.Chat Apps to interact with the host system.

Currently, a Rocket.Chat App can:

  • Listen to message events
    • before/after sent
    • before/after updated
    • before/after deleted
  • Listen to room events
    • before/after created
    • before/after deleted
  • Send messages to users and livechat visitors
  • Register new slash commands
  • Register new HTTP endpoints

Some features the Engine allows Apps to use:

  • Key-Value Storage system
  • App specific settings

Development environment with Rocket.Chat

When developing new functionalities, you need to integrate the local version of the Apps-Engine with your local version of Rocket.Chat.

First of all, make sure you've installed all required packages and compiled the changes you've made to the Apps-Engine, since that is what Rocket.Chat will execute:

npm install
npm run compile

Now, you need to setup a local Rocket.Chat server, so head to the project's README for instructions on getting started (if you haven't already). Make sure to actually clone the repo, since you will probably need to add some code to it in order to make your new functionality work.

After that, cd into Rocket.Chat folder and run:

meteor npm install PATH_TO_APPS_ENGINE

Where PATH_TO_APPS_ENGINE is the path to the Apps-Engine repo you've cloned.

That's it! Now when you start Rocket.Chat with the meteor command, it will use your local Apps-Engine instead of the one on NPM :)

Whenever you make changes to the engine, run npm run compile again - meteor will take care of restarting the server due to the changes.

Troubleshooting

  1. Sometimes, when you update the Apps-Engine code and compile it while Rocket.Chat is running, you might run on errors similar to these:
Unable to resolve some modules:

  "@rocket.chat/apps-engine/definition/AppStatus" in
/Users/dev/rocket.chat/Rocket.Chat/app/apps/client/admin/helpers.js (web.browser)

If you notice problems related to these missing modules, consider running:

  meteor npm install --save @rocket.chat/apps-engine

Simply restart the meteor process and it should be fixed.

  1. Sometimes when using meteor npm install PATH_TO_APPS_ENGINE will cause the following error :-
npm ERR! code ENOENT
npm ERR! syscall rename
npm ERR! path PATH_TO_ROCKETCHAT/node_modules/.staging/@rocket.chat/apps-engine-c7135600/node_modules/@babel/code-frame
npm ERR! dest PATH_TO_ROCKETCHAT/node_modules/.staging/@babel/code-frame-f3697825
npm ERR! errno -2
npm ERR! enoent ENOENT: no such file or directory, rename 'PATH_TO_ROCKETCHAT/node_modules/.staging/@rocket.chat/apps-engine-c7135600/node_modules/@babel/code-frame' -> 'PATH_TO_ROCKETCHAT/node_modules/.staging/@babel/code-frame-f3697825'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent 

Here PATH_TO_ROCKETCHAT is the path to the main rocketchat server repo in your system To correct this we reinstall the package once again deleting the previous package

~/Rocket.Chat$ rm -rf node_modules/@rocket.chat/apps-engine
~/Rocket.Chat$ cd PATH_TO_APP_ENGINE
~/Rocket.Chat.Apps-engine$ npm install
~/Rocket.Chat.Apps-engine$ cd PATH_TO_ROCKETCHAT
~/Rocket.Chat$ meteor npm install ../Rocket.Chat.Apps-engine

Implementer Needs to Implement:

  • src/server/storage/AppStorage
  • src/server/storage/AppLogStorage
  • src/server/bridges/*

Testing Framework:

Makes great usage of TypeScript and decorators: https://github.com/alsatian-test/alsatian/wiki

  • To run the tests do: npm run unit-tests
  • To generate the coverage information: npm run check-coverage
  • To view the coverage: npm run view-coverage

Rocket.Chat Apps TypeScript Definitions

Handlers

Handlers are essentially "listeners" for different events, except there are various ways to handle an event. When something happens there is pre and post handlers. The set of pre handlers happens before the event is finalized. The set of post handlers happens after the event is finalized. With that said, the rule of thumb is that if you are going to modify, extend, or change the data backing the event then that should be done in the pre handlers. If you are simply wanting to listen for when something happens and not modify anything, then the post is the way to go.

The order in which they happen is:

  • PreEventPrevent
  • PreEventExtend
  • PreEventModify
  • PostEvent

Here is an explanation of what each of them means:

  • Prevent: This is ran to determine whether the event should be prevented or not.
  • Extend: This is ran to allow extending the data without being destructive of the data (adding an attachment to a message for example).
  • Modify: This is ran and allows for destructive changes to the data (change any and everything).
  • PostEvent: Is mostly for simple listening and no changes can be made to the data.

Generating/Updating Documentation

To update or generate the documentation, please commit your changes first and then in a second commit provide the updated documentation.

Engage with us

Share your story

We’d love to hear about your experience and potentially feature it on our Blog.

Subscribe for Updates

Once a month our marketing team releases an email update with news about product releases, company related topics, events and use cases. Sign Up!

rocket.chat.apps-engine's People

Contributors

aditya-mitra avatar allanpazribeiro avatar cardoso avatar csuadev avatar d-gubert avatar debdutdeb avatar dependabot[bot] avatar dnouv avatar gdelavald avatar geekgonecrazy avatar ggazzo avatar graywolf336 avatar kevlehman avatar lmauromb avatar lucassartor avatar marceloschmidt avatar matheusbsilva137 avatar murtaza98 avatar pierre-lehnen-rc avatar ritwizsinha avatar rodrigok avatar rohan749 avatar sampaiodiego avatar shailesh351 avatar shiqimei avatar tapiarafael avatar tassoevan avatar thassiov avatar tiagoevanp avatar yuq-bian avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rocket.chat.apps-engine's Issues

Returning False in onEnable When Updating Doesn't Properly Update Status

If an App was previously installed and enabled, whenever you update it and return false on the method onEnable then the commands and handlers correctly don't get registered but the status of the App isn't correctly updated to be disabled. Returning false on the method onEnable should disable the App.

"method not implemented" when using RoomRead.getMembers(roomId)

Hi,

we are trying create an app that creates a room name dynamically via the usernames, but within the executePreRoomCreateModify we are not able to figure out the usernames.

We also first tried to use the usernames property but stumpled upon this issue #58

Is there a different way with an implemented method? Or any other idea on how to fix this?
:)

It also would help if we just had access to the IDs of the users within the room...

thanks in advance

Implement oEmbed Url Previews Handler

Idea

A Rocketlet can register a url it would like to add additional information to the request of the oEmbed information request.

Example

Rocketlet which allows for a GitHub authentication information to be attached to oEmbed requests out to GitHub, this way links to private repositories can have their information shown instead of showing nothing.

Question: does App have access to mongoose?

The question is, will the Application have access directly to mongoose.

It would be nice, but a little dangerous anyway. On the other hand, Rest API or Realtime API access would be nice, but weird since application works locally inside RocketChat.

Compiler Doesn't Complain on Invalid TypeScript References

Currently our internally compiler doesn't complain/error out whenever an Application contains invalid code and references. For example, we moved over to each handler being a promise so the return type is Promise<T> but if an old App is uploaded that only returns T it should fail but it doesn't.

Handle Rocketlets which error out during compile time

Currently Rocketlets which error out during compile time, for any numerous reasons, are ignored and not added anywhere that they can successfully be removed or updated and doing so with their id will actually error out. So, special provisions need to be made for Rocketlets which error out during compile time and enable time.

Implement the Persistent Storage Querying/Requests

Currently there are only two ways to query/request data from the persistent storage. First is via the id of the stored object and the second is if the data was associated with other objects. However, there is a need for some query support that is database agnostic (since Rocket.Chat will be moving away from Meteor in the next year or two and that opens the door for different backing database support). The idea here is to create either a query builder and/or provide classes/interfaces which will allow the database query to be built for the current database type being used.

Implement Scheduler

An Application needs the ability to schedule a task that runs at a configurable interval or at a specific time. This can be useful for several instances, such as a /remind command or a daily stand up question.

A task should only run on one server when there are several instances of Rocket.Chat running. To do this I am thinking of the following terminology (with more details coming later, as this is a rough brain dump issue post):

  • Workers
  • Worker IDs
  • Worker Grabbers
  • Manager
  • Queue
  • Time to Live

Some projects to look into that handle these things currently (reminder: the Apps are an abstraction layer from the database and backing system, thus all of these details are required even if the backing system already has it). meteor-syncd-cron and SteveJobs

Ability to add a page in the directory

As an app developer, I'd like to add an app which provides a dashboard of current activities in Rocket.Chat.
As this is a "global" (room independent) feature, I want to place it into the directory / discovery.

Is there an API for that?

Room usernames removal

Prune Old Logs

Auto prune logs for Apps after an extended amount of time.

Allow Dependencies on npm Packages

@graywolf336 commented on Tue May 15 2018

Currently Apps can not bring in third party packages in a supported manner (yes there are ways around it), however this is a big need and if we don't allow this then Apps will be severely limited. However, just allowing Apps to include any and every npm package will open the door to security issues and even malware packages.

So, we need a way to provide "secure" way for Apps to define the npm packages they use. This will be a multi-step process. First up, we will add a npmDepends configuration section of the App.json configuration file. Then whenever someone submits an App to the Marketplace we will look through that section and ensure they seem to be trusted modules. After it has been marked as reviewed and "trusted", an automated system will package in the npm modules (we will need to look into the license restrictions here) into the App.

However, there will be Apps that want to not distribute themselves via the Marketplace for various reasons (let's say an in-house App). Due to this, we will provide a way for Apps to package the npm modules themselves but a big, not so nice, warning will be displayed when installing and enabling the App that warns of potential risks associated with that App.


I would like the thoughts of any and everyone on this manner. Does this make sense and is it logical to do this? Yes, the Apps Marketplace will already have a manual review process so adding one more task to it just makes sense.


@timkinnane commented on Wed May 16 2018

Hey Bradley, on a technical note, is npmDepends a common approach or specific to Meteor? My understanding is that the apps will not be Meteor packages, so why not just use standard package.json manifest? Seems like reinventing the wheel otherwise?

Generally, I would err on the side of less restriction rather than the tight controls you're suggesting. Every node package is just the tip of its own tree of dependencies, there's no feasible way to enforce security standards for all the code in use, so it seems arbitrary and unnecessarily restrictive to enforce oversight at that level. Not to mention an intensive task for manual reviewers.

I'd suggest just listing the included dependencies on the app's marketplace profile, perhaps with some stats for each package, e.g: number of downloads, number of dependents, up to date dependencies (david-dm.org badge). There's services available to link to or integrate, for auditing an NPM package too, e.g. packagequality.
Just found this article sharing some similar approaches: https://bytearcher.com/articles/evaluating-packages-1-check-community/

I think, in the interest of keeping the system open for app creators, it's reasonable to assume some responsibility lies with the people installing the app to make their own determination on it's quality. If you make the controls too restrictive, the approval process will become a bottleneck and add to the cognitive load of how to navigate this ecosystem and get something out there, which is already high coming in.


@JSzaszvari commented on Thu May 17 2018

@timkinnane Agreed ++


@graywolf336 commented on Fri May 18 2018

Thanks for the input guys πŸ‘ we will keep this in mind when actually starting to work on this. :)


@graywolf336 commented on Wed May 30 2018

To do: Evaluate how vscode's extension manifest (as the App.json was influenced by it) https://code.visualstudio.com/docs/extensionAPI/extension-manifest

Support for client-side code

It would be useful to have some capability for running code in the client-side with local storage and external request options.

The first use case that comes to mind is custom E2E encryption solutions, but also other things would be possible like local translation.

I think we can begin by implementing some hooks like:

function preMessageSend(Message, IRead, IModify, IPersistence) {
    // client will run this code when sending a message and possibly transform it
    return Message // return false to prevent message from being sent
}

function onMessageReceive(Message, IRead, IModify, IPersistence) {
    // client will run this code when message is received and possibly transform it
    return Message // return false to filter out the message?
}

This should not be hard to implement on iOS or Android, since both have powerful native JavaScript interop.

Decide on how UI Pieces Will Be Implemented

@graywolf336 commented on Tue Apr 03 2018

Right now there is a discussion which needs to happen about how various UI pieces will be implemented and structured. For example, the contextual bar is now ready inside of Rocket.Chat however we need to decide how we are going to have them activated inside of Rocket.Chat.

  • Are we going to use a framework?
    • Polymer
    • React
    • Shadow dom
    • iframe
    • or..?
  • How focused on security are we going to be?
  • Do we limit the scope of the App's JavaScript for the element?
  • How will icons be generated? See: RocketChat/Rocket.Chat.Apps-ts-definition#19

These are all questions which come to my mind at this current point in time, however there are more that I need to dig up. Feel free to append other questions.


@mrsimpson commented on Tue Apr 03 2018

I'm definitely not the one to make a decision on that. Still, as you pointed me to it:

I don't know all the frameworks nor their pros and cons. However, I feel that going for React is a good choice: React native, native ES6, components which can be defined by an interface, high traction on the dev commmunity, similar paradigms compared to Blaze...

I'd expect a React component I can derive from for each location (the sidebar, primarily, maybe also a message-oriented view as implemented here) the component shall appear in (maybe a stateless in the beginning, a stateful later on).

Imho, React is a safe bet (maybe except licensing, I'm not an expert on that) and it's more important to start soon than finding a 1% better match.

Just my 5 cents


@mrsimpson commented on Tue Apr 03 2018

ad how to limit the scope: I'd go for constructor injection on the component. Of course, somebody requiring an unwanted lib can hardly be prevented - unless you prevent and loading of libs in general. So one opportunity was to only bind dependencies via npm. Those, you can at least parse and patch more easily


@mrsimpson commented on Tue Apr 03 2018

ad icons: I'd expect each Rocketlet to provide some assets from which the icons can be loaded (CSS should come via an API). A set of icons which are already loaded for all of them was convenient for a start though.

Implement OAuth Providers

The first version could be only setting up custom OAuth providers, being able to set all its fields should be enough (see screenshot bellow).

But the ideal implementation for me should be being able to register loginHandlers like this

image

Disabling Slash Command Doesn't Remove it Client Side

To get around #70, I am now disabling and enabling the slash command based upon a setting instead of just disabling the entire App. However, when the App disables the Slash Command it doesn't get disabled in the clients web browsers or anything. Watching the websocket and no messages/frames come through which tell it to disable commands, so something is missing server side.

Add Instance Documentation

@graywolf336 commented on Wed Apr 11 2018

Add documentation which states that storing items in memory inside of the App is not recommended at all because a Rocket.Chat server can have several instances and each server has its own instance of an app. If the data, such as confirmation for context, needs to be shared between the different instances then the persistence accessor needs to be used.

Don't Enable an App When Required Settings Are Missing

Right now an App is enabled without any thought for the settings it provides and their required property. If an App contains settings which are required and the user has not provided them, or the packageValue isn't provided by the App, then don't enable the App and don't allow the user to enable the App.

Add complementary information to apps descriptor file

@theorenck commented on Thu Jun 07 2018

Based on a discussion with @thiagosanchz about what information we need to have in the app description page, we would like to suggest some improvements:

  • description: this one could be a long description limited to just one or two paragraphs long
  • summary: just a brief description, possibly just one line, just about one or two phases long
  • website: If the author want to better "sell" his app idea, he should do this in a page of his own
  • categories: on the Marketplace designs we have the concept of categories, shouldn't them be inside the descriptor file?

Thoughts?


@graywolf336 commented on Fri Jun 08 2018

See the discussion on #49. We will be moving to package.json and taking a page out of VSCode Extension manifest file book on how to approach this. However, due to moving away from app.json file as the manifest file, we will have to change the properties and thus this will change the internals of the framework system which is not a small change.

VSCode Debugging

As this project gets worked on more and more, sometimes being able to step through and debug the code would be beyond useful. However, due to the project setup the configuration of the debugger isn't exactly the easiest.

There are technically two typescript projects in this repository. The first one is the actual engine code, located under the src folder which compiles down to the dist folder. Then the second one is the testing server piece which is used to try out various pieces of the engine while development happens (such as loading, installing, and updating apps). The development server piece is located under dev folder and compiles down to dev-dist. Then gulp will launch the generated dev-dist/server.js file.

[BUG] Unable to start RocketChat under windows

Unable to run RocketChat under Windows due exception:

...
 Exception in callback of async function: Error: Could not find the Apps TypeScript Definition Package Version!
...

because of AppPackageParser.ts@151

...
        const prodLocation = __dirname.split('@rocket.chat/apps-engine')[0] + '@rocket.chat/apps-ts-definition/package.json';
...

Splitting path by substring contains / - bad idea.

Implement the Permissions

Ability to create, get, and check permissions.

This would be amazing for commands that only need to be given to certain people.

Allow sending persistent message to groups, channels & dms without inviting bot

The only ways I found to send messages with Apps were:

Via User or Bot User. Con: They need to be added to groups & don't work on DMs
Via notifyRoom() or notifyUser(). Con: Messages are not persistent & say 'only you can see this message' even if you used notifyRoom()

I think we need an APP user type that can send messages in channels, groups & dms without having to be explicitly invited.

Ref: cardoso/Rocket.Chat.Dropbox.Paper#1

Visualization in tab bar

The right hand side panel is the place in Rocket.Chat where additional information for the context of the current conversation are being displayed (e. g. who are the current members, what are the pinned messages and so on).
Informative Apps should be able to claim space in there.

For a better illustration, here's an "app" which we provide in our custom fork:
image

The app detects via NLP what the conversation is currently about and performs implicit search.
As of today, we implemented this widget independent of Rocket.Chat: It's a jQuery based UI which simply attaches to a DOM node provided as a tab and then interacts via DDP with Rocket.Chat.

Personally, I believe that this would be an option for most desktop apps. Would providing an (empty) tab be a first step in the apps as well?

Scan the code for what the Rocketlet provides

One of the major USPs of Rocket.Chat compared to others is it's availability on premise with enterprise features for companies. I'd not install a single Rocketlet if I was unsure what it can do.

Method not implemented exception when calling configurationModify.serverSettings.modifySettings()

I have written a small app which replaces chat messages which contain specific strings with links to our issue tracker. When you install the app, it has a URL which you need to provide. Once you have updated the url in the onSettingUpdated will send a request to the issue tracker's rest api and fetch all of the active projects so that when you write ABC-123 it would replace it with a link to the issue.

The issue I'm facing is that when RocketChat restarts, the projects are not preserved. I decided to create another configuration setting in the extendConfiguration which is hidden and store the data there, but I'm receiving the following exception:

  "{\"stack\":\"Error: Method not implemented.
    at Promise.asyncApply (/app/bundle/programs/server/packages/rocketchat_apps.js:1245:13)
    at /app/bundle/programs/server/npm/node_modules/meteor/promise/node_modules/meteor-promise/fiber_pool.js:43:40
\",\"message\":\"Method not implemented.\"}"

Am I doing it wrong or is this really not implemented? Is there any actual documentation of how all this should be working - I don't seem to be finding it on the website?

Here's my code:

import {IConfigurationExtend,IConfigurationModify,IEnvironmentRead,IHttp,IHttpResponse,ILogger,IMessageBuilder,IPersistence,IRead} from '@rocket.chat/apps-engine/definition/accessors';
import {App} from '@rocket.chat/apps-engine/definition/App';
import {IMessage, IPreMessageSentModify} from '@rocket.chat/apps-engine/definition/messages';
import {IAppInfo} from '@rocket.chat/apps-engine/definition/metadata';
import {ISetting, SettingType} from "@rocket.chat/apps-engine/definition/settings";

export class YouTrackApp extends App implements IPreMessageSentModify {

    readonly SETTING_YOUTRACK_URL = 'Youtrack_URL';
    readonly SETTING_YOUTRACK_PROJECTS = 'Youtrack_PROJCETS';

    private projects = [];

    protected constructor(info: IAppInfo, logger: ILogger) {
        super(info, logger);
    }

    public async checkPreMessageSentModify(message: IMessage, read: IRead, http: IHttp): Promise<boolean> {
        const url = (await read.getEnvironmentReader().getSettings().getValueById(this.SETTING_YOUTRACK_URL));

        if (url !== null && url !== '' && this.projects.length > 0 && message.text) {
            const match = message.text.match(this.getRegex());
            return match !== null && match.length > 0;
        }

        return false;
    }

    public async executePreMessageSentModify(message: IMessage, builder: IMessageBuilder, read: IRead, http: IHttp, persistence: IPersistence): Promise<IMessage> {
        const url = (await read.getEnvironmentReader().getSettings().getValueById(this.SETTING_YOUTRACK_URL));

        if (!url || this.projects.length < 1) {
            return message;
        }

        if (message.text) {
            const regex = this.getRegex();
            const match = Array.from(new Set(message.text.match(regex)));

            if (match && match.length > 0) {

                let text = message.text;

                for (let i = 0; i < match.length; i++) {
                    const issue = match[i];
                    let title = await this.getIssueSummary(issue, url, http);

                    // This could happen on error or if the issue is private.
                    if (title === false) {
                        title = '';
                    }

                    this.getLogger().debug(title);
                    this.getLogger().debug(message.text);

                    // @ts-ignore
                    text = text.replace(new RegExp(issue, 'gi'), '[' + issue.toUpperCase() + ' ' + title + '](' + url + '/issue/' + issue + ')');
                }

                return builder.setText(text).getMessage();
            }
        }

        return message;
    }

    public async onSettingUpdated(setting: ISetting, configurationModify: IConfigurationModify, read: IRead, http: IHttp): Promise<void> {
        if (setting.id === this.SETTING_YOUTRACK_URL) {
            // Ignore this error - setting is actually an array of settings.
            const url = setting.value;

            this.getLogger().debug(setting);
            this.getLogger().debug(setting.value);

            read.getPersistenceReader().read('test');

            if (url !== null && url !== '') {
                const fetchProjects = await this.getProjects(url, http);

                if (fetchProjects) {
                    this.projects = fetchProjects;
                } else {
                    this.projects = [];
                }
            } else {
                this.projects = [];
            }

            await configurationModify.serverSettings.modifySetting({
                id: this.SETTING_YOUTRACK_PROJECTS,
                type: SettingType.STRING,
                value: JSON.stringify(this.projects),
                packageValue: null,
                required: false,
                public: false,
                hidden: true,
                i18nLabel: ''
            });

            this.getLogger().debug("Projects:");
            this.getLogger().debug(this.projects);
            this.getLogger().debug("Regex matcher:");
            this.getLogger().debug(this.getRegex().toString());
        }
    }

    private getRegex() {
        const expr = '(' + this.projects.join('|') + ')-([0-9]+)';
        return new RegExp(expr, 'gi');
    }

    private async getIssueSummary(issue, url, http: IHttp) {
        const restUrl = url + '/rest/issue/' + issue;

        const response = await http
            .get(restUrl, {headers: {"accept": "application/json"}})
            .then((response: IHttpResponse) => {
                try {
                    if (response.content) {
                        const summary = JSON.parse(response.content).field.filter(v => v.name === 'summary').map(v => v.value);
                        this.getLogger().debug("Youtrack issue (" + issue + ") summary retrieved: ", summary);
                        return summary;
                    } else {
                        this.getLogger().error("Response content cannot be parsed!");
                        this.getLogger().error(response.content);

                        return false;
                    }
                } catch (e) {
                    this.getLogger().error("Failed to parse response from " + restUrl);
                    this.getLogger().error(e);

                    return false;
                }
            }, (reason: any) => {
                this.getLogger().error("Failed to request " + restUrl);
                this.getLogger().error(reason);

                return false;
            });

        if (response !== false) {
            return response;
        } else {
            this.getLogger().error('Could not fetch youtrack issue summary!');
            return false;
        }
    }

    private async getProjects(url, http: IHttp) {
        const projectsUrl = url + '/rest/project/all';

        const response = await http
            .get(projectsUrl, {headers: {"accept": "application/json"}})
            .then((response: IHttpResponse) => {
                try {
                    if (response.content) {
                        this.getLogger().debug("Youtrack projects: ");
                        this.getLogger().debug(this.projects);

                        return JSON.parse(response.content).map(v => v.shortName);
                    } else {
                        this.getLogger().error("Response content cannot be parsed!");
                        this.getLogger().error(response.content);

                        return false;
                    }
                } catch (e) {
                    this.getLogger().error("Failed to parse response from " + projectsUrl);
                    this.getLogger().error(e);

                    return false;
                }
            }, (reason: any) => {
                this.getLogger().error("Failed to request " + projectsUrl);
                this.getLogger().error(reason);

                return false;
            });

        if (response !== false) {
            return response;
        } else {
            this.getLogger().error('Could not fetch youtrack projects!');
            return false;
        }
    }

    protected async extendConfiguration(configuration: IConfigurationExtend, environmentRead: IEnvironmentRead): Promise<void> {
        await configuration.settings.provideSetting({
            id: this.SETTING_YOUTRACK_URL,
            type: SettingType.STRING,
            packageValue: null,
            required: true,
            public: false,
            i18nLabel: this.SETTING_YOUTRACK_URL,
            i18nDescription: 'Youtrack_URL_Description',
        });

        await configuration.settings.provideSetting({
            id: this.SETTING_YOUTRACK_PROJECTS,
            type: SettingType.STRING,
            packageValue: null,
            required: false,
            public: false,
            hidden: true,
            i18nLabel: ''
        });

        try{
            this.projects = JSON.parse(await environmentRead.getServerSettings().getValueById(this.SETTING_YOUTRACK_PROJECTS))
        } catch (e) {
            this.projects = [];
        }
    }
}

Allow an App to register a "user/bot"

@graywolf336 commented on Thu Jan 11 2018

Allow the Rocket.Chat Apps to register a user, aka a bot, that will then populate the auto-completes when you go to tag a user. This will allow applications to "impersonate" a user and "converse" with them.


@mrsimpson commented on Thu Jan 11 2018

I believe that this property (reading messages and impersonating a response) could turn RocketChat into a bot platform and give a spin at the Rocket.Chat Apps even with the limited (text-only) scope. Feature-wise, it’s not much more compared to a slash-command (a textual interface), but it would allow bot developers to even capture a conversational state inside or outside of Rocket.Chat


@timkinnane commented on Thu Jan 11 2018

@mrsimpson Most of what you're talking about is how the platform already works to support Hubot for instance. Via the hubot-rocketchat adapter, which uses Asteroid to log in and set up channel subscriptions and send/receive.

@graywolf336 +1 on this. But I'll also flag that it ties into something I'd like to do for general bot support and testing. I think the creation process could be streamlined for bots, such as core issue 3818 and other mods to register bots programatically more simply than users.

For both bot and app testing, I'm thinking about a feature that would support creation of temporary users in private channels. So that neither the channel or users are visible to the community and they can be easily wiped away. I know this is overloading this particular ticket, just giving some context before anyone jumps to a solution.


@mrsimpson commented on Wed Jan 17 2018

@graywolf336 funny enough, guggy itself has pictured it like that
http://s3.amazonaws.com/guggyresources/guggyexample.gif

Add handlers for message updates

Right now a user can bypass an IPreMessageSentPrevent by sending a message then editing it

So we would need something like IPreMessageUpdatedPrevent etc

Investigate Using Worker Threads

The age old question is being asked already, how can we improve the performance of the entire Apps framework? Sure the work being done on making everything "async" and using "promises" is great, however Rocket.Chat is more or less single threaded and also one instance of Rocket.Chat shares one message queue with everything happening. Thus when a Rocket.Chat App runs, and since we are promising everything it is now harder to enforce a timeout on execution of an App, what can we do to improve the performance and ensure one single App doesn't negatively effect, on purpose or accident, the performance of a server.

I hereby propose that we look into using this package: https://www.npmjs.com/package/threads

Some R&D is required before we go and "blindly" start using it...who knows, maybe we can use this in other areas of Rocket.Chat...integrations anyone? πŸ€”

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.