mirror of
https://github.com/matrix-org/matrix-hookshot.git
synced 2025-03-10 21:19:13 +00:00
Minor grammar corrections in code and documentation (#267)
* setupWidget -> setUpWidget * setup -> (to) set up; add full stops * setupAdminRoom -> setUpAdminRoom * it's -> its * Create 267.feature * log in to; Currently, ; full stops * Improve copy of error messages * Update 267.feature * Rename 267.feature to 267.misc Co-authored-by: Will Hunt <will@half-shot.uk>
This commit is contained in:
parent
14abb011b6
commit
4060ded7f8
2
.github/workflows/docker-hub-latest.yml
vendored
2
.github/workflows/docker-hub-latest.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Login to Docker Hub
|
- name: Log in to Docker Hub
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v1
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
|
2
.github/workflows/docker-hub-release.yml
vendored
2
.github/workflows/docker-hub-release.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Get release tag
|
- name: Get release tag
|
||||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
- name: Login to Docker Hub
|
- name: Log in to Docker Hub
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v1
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
|
@ -65,7 +65,7 @@ Bugfixes
|
|||||||
- Fix a bug which caused GitHub "ready for review" events to be unhandled. ([\#149](https://github.com/matrix-org/matrix-hookshot/issues/149))
|
- Fix a bug which caused GitHub "ready for review" events to be unhandled. ([\#149](https://github.com/matrix-org/matrix-hookshot/issues/149))
|
||||||
- Fix a bug preventing `!hookshot jira project` from working ([\#166](https://github.com/matrix-org/matrix-hookshot/issues/166))
|
- Fix a bug preventing `!hookshot jira project` from working ([\#166](https://github.com/matrix-org/matrix-hookshot/issues/166))
|
||||||
- Fix a few issues preventing GitHub notifications from working ([\#173](https://github.com/matrix-org/matrix-hookshot/issues/173))
|
- Fix a few issues preventing GitHub notifications from working ([\#173](https://github.com/matrix-org/matrix-hookshot/issues/173))
|
||||||
- Fixed an issue where the bridge bot would change it's displayname if a webhook event is handled while `generic.userIdPrefix` is not set in the config. ([\#215](https://github.com/matrix-org/matrix-hookshot/issues/215))
|
- Fixed an issue where the bridge bot would change its displayname if a webhook event is handled while `generic.userIdPrefix` is not set in the config. ([\#215](https://github.com/matrix-org/matrix-hookshot/issues/215))
|
||||||
- Remove nonfunctional `gitlab notifications toggle` command. ([\#226](https://github.com/matrix-org/matrix-hookshot/issues/226))
|
- Remove nonfunctional `gitlab notifications toggle` command. ([\#226](https://github.com/matrix-org/matrix-hookshot/issues/226))
|
||||||
|
|
||||||
|
|
||||||
|
1
changelog.d/267.misc
Normal file
1
changelog.d/267.misc
Normal file
@ -0,0 +1 @@
|
|||||||
|
Make some grammar corrections in code, chat notices and documentation.
|
@ -108,11 +108,11 @@ Each permission set can have a services. The `service` field can be:
|
|||||||
- `*`, for any service.
|
- `*`, for any service.
|
||||||
|
|
||||||
The `level` can be:
|
The `level` can be:
|
||||||
- `commands` Can run commands within connected rooms, but NOT log into the bridge.
|
- `commands` Can run commands within connected rooms, but NOT log in to the bridge.
|
||||||
- `login` All the above, and can also log into the bridge.
|
- `login` All the above, and can also log in to the bridge.
|
||||||
- `notifications` All the above, and can also bridge their notifications.
|
- `notifications` All the above, and can also bridge their notifications.
|
||||||
- `manageConnections` All the above, and can create and delete connections (either via the provisioner, setup commands, or state events).
|
- `manageConnections` All the above, and can create and delete connections (either via the provisioner, setup commands, or state events).
|
||||||
- `admin` All permissions. Currently there are no admin features so this exists as a placeholder.
|
- `admin` All permissions. Currently, there are no admin features so this exists as a placeholder.
|
||||||
|
|
||||||
When permissions are checked, if a user matches any of the permission set and one
|
When permissions are checked, if a user matches any of the permission set and one
|
||||||
of those grants the right level for a service, they are allowed access. If none of the
|
of those grants the right level for a service, they are allowed access. If none of the
|
||||||
@ -134,7 +134,7 @@ permissions:
|
|||||||
services:
|
services:
|
||||||
- service: github
|
- service: github
|
||||||
level: manageConnections
|
level: manageConnections
|
||||||
# Allow users on this domain to login to jira and github.
|
# Allow users on this domain to log in to jira and github.
|
||||||
- actor: support.example.com
|
- actor: support.example.com
|
||||||
services:
|
services:
|
||||||
- service: jira
|
- service: jira
|
||||||
|
@ -29,7 +29,7 @@ The `accessToken` should be the personal access token for your account.
|
|||||||
|
|
||||||
The `passcode` should be a randomly generated code which is used to authenticate requests from Figma.
|
The `passcode` should be a randomly generated code which is used to authenticate requests from Figma.
|
||||||
|
|
||||||
The bridge will automatically setup a webhook on Figma for you upon startup, and will automatically reconfigure that webhook if the `publicUrl` or `passcode` changes.
|
The bridge will automatically set up a webhook on Figma for you upon startup, and will automatically reconfigure that webhook if the `publicUrl` or `passcode` changes.
|
||||||
|
|
||||||
## Next steps
|
## Next steps
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ jira:
|
|||||||
... # See below
|
... # See below
|
||||||
```
|
```
|
||||||
|
|
||||||
You can omit the `oauth` section if you are not planning to allow users to login and use interactive features (i.e. webhook only mode).
|
You can omit the `oauth` section if you are not planning to allow users to log in and use interactive features (i.e. webhook only mode).
|
||||||
|
|
||||||
## Connecting Matrix users to JIRA
|
## Connecting Matrix users to JIRA
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ jira:
|
|||||||
# The path to your webhooks listener on the "/jira/oauth" path.
|
# The path to your webhooks listener on the "/jira/oauth" path.
|
||||||
redirect_uri: http://localhost:5065/jira/oauth
|
redirect_uri: http://localhost:5065/jira/oauth
|
||||||
```
|
```
|
||||||
To start with, setup your JIRA instance to support OAuth.
|
To start with, set up your JIRA instance to support OAuth.
|
||||||
|
|
||||||
|
|
||||||
1. Open the **Administration** page for your JIRA instance.
|
1. Open the **Administration** page for your JIRA instance.
|
||||||
@ -104,7 +104,7 @@ To start with, setup your JIRA instance to support OAuth.
|
|||||||
1. Enter your `consumerKey` from the config file above.
|
1. Enter your `consumerKey` from the config file above.
|
||||||
2. The `consumerName` can be anything, but will be visible to users of your app. You could use something like `Matrix`, `Hookshot` or anything else.
|
2. The `consumerName` can be anything, but will be visible to users of your app. You could use something like `Matrix`, `Hookshot` or anything else.
|
||||||
3. The `publicKey` can be generated by running `openssl rsa -in jira_privatekey.pem -pubout` on the key you created earlier.
|
3. The `publicKey` can be generated by running `openssl rsa -in jira_privatekey.pem -pubout` on the key you created earlier.
|
||||||
7. Congratulations, you now have OAuth setup.
|
7. Congratulations, you now have OAuth set up.
|
||||||
|
|
||||||
## Next steps
|
## Next steps
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ To authenticate with a personal access token:
|
|||||||
You can authenticate with GitLab by supplying a Personal Access Token. OAuth-style authentication isn't supported
|
You can authenticate with GitLab by supplying a Personal Access Token. OAuth-style authentication isn't supported
|
||||||
yet.
|
yet.
|
||||||
|
|
||||||
- You will need to have configured a GitLab instance in your config.yml for the instance you want to login to.
|
- You will need to have configured a GitLab instance in your config.yml for the instance you want to log in to.
|
||||||
- Open https://%instance%/-/profile/personal_access_tokens (GitLab > User Settings > Access Tokens), where instance is your GitLab instance address.
|
- Open https://%instance%/-/profile/personal_access_tokens (GitLab > User Settings > Access Tokens), where instance is your GitLab instance address.
|
||||||
- For the public GitLab server, this would be "gitlab.com"
|
- For the public GitLab server, this would be "gitlab.com"
|
||||||
- Give it a good name, and a sensible expiration date. For scopes you will need to tick `api`.
|
- Give it a good name, and a sensible expiration date. For scopes you will need to tick `api`.
|
||||||
@ -49,7 +49,7 @@ yet.
|
|||||||
|
|
||||||
## JIRA
|
## JIRA
|
||||||
|
|
||||||
You can login to JIRA via OAuth. This means you will need to have configured OAuth support in your `config.yml`, and
|
You can log in to JIRA via OAuth. This means you will need to have configured OAuth support in your `config.yml`, and
|
||||||
have the endpoints required accessible from the internet. Authentication is required when trying to bridge JIRA resources into rooms.
|
have the endpoints required accessible from the internet. Authentication is required when trying to bridge JIRA resources into rooms.
|
||||||
|
|
||||||
- Say `jira login` to get the URL to authenticate via.
|
- Say `jira login` to get the URL to authenticate via.
|
||||||
|
@ -160,7 +160,7 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
private async setGitHubNotificationsStateParticipating() {
|
private async setGitHubNotificationsStateParticipating() {
|
||||||
const newData = await this.saveAccountData((data) => {
|
const newData = await this.saveAccountData((data) => {
|
||||||
if (!data.github?.notifications?.enabled) {
|
if (!data.github?.notifications?.enabled) {
|
||||||
throw Error('Notifications are not enabled')
|
throw Error('Notifications are not enabled.')
|
||||||
}
|
}
|
||||||
const oldState = data.github?.notifications?.participating ?? false;
|
const oldState = data.github?.notifications?.participating ?? false;
|
||||||
return {
|
return {
|
||||||
@ -174,17 +174,17 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
if (newData.github?.notifications?.participating) {
|
if (newData.github?.notifications?.participating) {
|
||||||
return this.sendNotice(`Filtering for events you are participating in`);
|
return this.sendNotice(`Filtering for events you are participating in.`);
|
||||||
}
|
}
|
||||||
return this.sendNotice(`Showing all events`);
|
return this.sendNotice(`Showing all events.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@botCommand("github notifications", {help: "Show the current notification settings", category: "github"})
|
@botCommand("github notifications", {help: "Show the current notification settings", category: "github"})
|
||||||
public async getGitHubNotificationsState() {
|
public async getGitHubNotificationsState() {
|
||||||
if (!this.notificationsEnabled("github")) {
|
if (!this.notificationsEnabled("github")) {
|
||||||
return this.sendNotice(`Notifications are disabled`);
|
return this.sendNotice(`Notifications are disabled.`);
|
||||||
}
|
}
|
||||||
return this.sendNotice(`Notifications are enabled, ${this.notificationsParticipating("github") ? "Showing only events you are particiapting in" : "Showing all events"}`);
|
return this.sendNotice(`Notifications are enabled, ${this.notificationsParticipating("github") ? "Showing only events you are particiapting in." : "Showing all events."}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -192,7 +192,7 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
// @ts-ignore - property is used
|
// @ts-ignore - property is used
|
||||||
private async listProjects(username?: string, repo?: string) {
|
private async listProjects(username?: string, repo?: string) {
|
||||||
if (!this.config.github) {
|
if (!this.config.github) {
|
||||||
return this.sendNotice("The bridge is not configured with GitHub support");
|
return this.sendNotice("The bridge is not configured with GitHub support.");
|
||||||
}
|
}
|
||||||
const octokit = await this.tokenStore.getOctokitForUser(this.userId);
|
const octokit = await this.tokenStore.getOctokitForUser(this.userId);
|
||||||
if (!octokit) {
|
if (!octokit) {
|
||||||
@ -218,7 +218,7 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
})).data;
|
})).data;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
log.warn(`Failed to fetch projects:`, ex);
|
log.warn(`Failed to fetch projects:`, ex);
|
||||||
return this.sendNotice(`Failed to fetch projects due to an error. See logs for details`);
|
return this.sendNotice(`Failed to fetch projects due to an error. See logs for details.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = `Projects for ${username}:\n${FormatUtil.projectListing(res)}\n`;
|
const content = `Projects for ${username}:\n${FormatUtil.projectListing(res)}\n`;
|
||||||
@ -234,7 +234,7 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
// @ts-ignore - property is used
|
// @ts-ignore - property is used
|
||||||
private async listProjects(org: string, repo?: string) {
|
private async listProjects(org: string, repo?: string) {
|
||||||
if (!this.config.github) {
|
if (!this.config.github) {
|
||||||
return this.sendNotice("The bridge is not configured with GitHub support");
|
return this.sendNotice("The bridge is not configured with GitHub support.");
|
||||||
}
|
}
|
||||||
const octokit = await this.tokenStore.getOctokitForUser(this.userId);
|
const octokit = await this.tokenStore.getOctokitForUser(this.userId);
|
||||||
if (!octokit) {
|
if (!octokit) {
|
||||||
@ -254,10 +254,10 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
}));
|
}));
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
if (ex.status === 404) {
|
if (ex.status === 404) {
|
||||||
return this.sendNotice('Not found');
|
return this.sendNotice(`${repo ? "Repository" : "Org"} does not exist.`);
|
||||||
}
|
}
|
||||||
log.warn(`Failed to fetch projects:`, ex);
|
log.warn(`Failed to fetch projects:`, ex);
|
||||||
return this.sendNotice(`Failed to fetch projects due to an error. See logs for details`);
|
return this.sendNotice(`Failed to fetch projects due to an error. See logs for details.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = `Projects for ${org}:\n` + res.data.map(r => ` - ${FormatUtil.projectListing([r])}\n`).join("\n");
|
const content = `Projects for ${org}:\n` + res.data.map(r => ` - ${FormatUtil.projectListing([r])}\n`).join("\n");
|
||||||
@ -273,7 +273,7 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
// @ts-ignore - property is used
|
// @ts-ignore - property is used
|
||||||
private async openProject(projectId: string) {
|
private async openProject(projectId: string) {
|
||||||
if (!this.config.github) {
|
if (!this.config.github) {
|
||||||
return this.sendNotice("The bridge is not configured with GitHub support");
|
return this.sendNotice("The bridge is not configured with GitHub support.");
|
||||||
}
|
}
|
||||||
const octokit = await this.tokenStore.getOctokitForUser(this.userId);
|
const octokit = await this.tokenStore.getOctokitForUser(this.userId);
|
||||||
if (!octokit) {
|
if (!octokit) {
|
||||||
@ -287,10 +287,10 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
this.emit('open.project', project.data);
|
this.emit('open.project', project.data);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
if (ex.status === 404) {
|
if (ex.status === 404) {
|
||||||
return this.sendNotice('Not found');
|
return this.sendNotice('Project does not exist.');
|
||||||
}
|
}
|
||||||
log.warn(`Failed to fetch project:`, ex);
|
log.warn(`Failed to fetch project:`, ex);
|
||||||
return this.sendNotice(`Failed to fetch project due to an error. See logs for details`);
|
return this.sendNotice(`Failed to fetch project due to an error. See logs for details.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,7 +299,7 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
private async listDiscussions(owner: string, repo: string, numberStr: string) {
|
private async listDiscussions(owner: string, repo: string, numberStr: string) {
|
||||||
const number = parseInt(numberStr);
|
const number = parseInt(numberStr);
|
||||||
if (!this.config.github) {
|
if (!this.config.github) {
|
||||||
return this.sendNotice("The bridge is not configured with GitHub support");
|
return this.sendNotice("The bridge is not configured with GitHub support.");
|
||||||
}
|
}
|
||||||
const octokit = await this.tokenStore.getOctokitForUser(this.userId);
|
const octokit = await this.tokenStore.getOctokitForUser(this.userId);
|
||||||
if (!octokit) {
|
if (!octokit) {
|
||||||
@ -311,10 +311,10 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
this.emit('open.discussion', owner, repo, discussions);
|
this.emit('open.discussion', owner, repo, discussions);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
if (ex.status === 404) {
|
if (ex.status === 404) {
|
||||||
return this.sendNotice('Not found');
|
return this.sendNotice('Discussion does not exist.');
|
||||||
}
|
}
|
||||||
log.warn(`Failed to fetch discussions:`, ex);
|
log.warn(`Failed to fetch discussions:`, ex);
|
||||||
return this.sendNotice(`Failed to fetch discussions due to an error. See logs for details`);
|
return this.sendNotice(`Failed to fetch discussions due to an error. See logs for details.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -325,7 +325,7 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
// @ts-ignore - property is used
|
// @ts-ignore - property is used
|
||||||
private async gitLabOpenIssue(url: string) {
|
private async gitLabOpenIssue(url: string) {
|
||||||
if (!this.config.gitlab) {
|
if (!this.config.gitlab) {
|
||||||
return this.sendNotice("The bridge is not configured with GitLab support");
|
return this.sendNotice("The bridge is not configured with GitLab support.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlResult = GitLabClient.splitUrlIntoParts(this.config.gitlab.instances, url);
|
const urlResult = GitLabClient.splitUrlIntoParts(this.config.gitlab.instances, url);
|
||||||
@ -336,7 +336,7 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
const instance = this.config.gitlab.instances[instanceName];
|
const instance = this.config.gitlab.instances[instanceName];
|
||||||
const client = await this.tokenStore.getGitLabForUser(this.userId, instance.url);
|
const client = await this.tokenStore.getGitLabForUser(this.userId, instance.url);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
return this.sendNotice("You have not added a personal access token for GitLab");
|
return this.sendNotice("You have not added a personal access token for GitLab.");
|
||||||
}
|
}
|
||||||
const getIssueOpts = {
|
const getIssueOpts = {
|
||||||
issue: parseInt(parts[parts.length-1]),
|
issue: parseInt(parts[parts.length-1]),
|
||||||
@ -351,11 +351,11 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
public async setGitLabPersonalAccessToken(instanceName: string, accessToken: string) {
|
public async setGitLabPersonalAccessToken(instanceName: string, accessToken: string) {
|
||||||
let me: GetUserResponse;
|
let me: GetUserResponse;
|
||||||
if (!this.config.gitlab) {
|
if (!this.config.gitlab) {
|
||||||
return this.sendNotice("The bridge is not configured with GitLab support");
|
return this.sendNotice("The bridge is not configured with GitLab support.");
|
||||||
}
|
}
|
||||||
const instance = this.config.gitlab.instances[instanceName];
|
const instance = this.config.gitlab.instances[instanceName];
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
return this.sendNotice("The bridge is not configured for this GitLab instance");
|
return this.sendNotice("The bridge is not configured for this GitLab instance.");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const client = new GitLabClient(instance.url, accessToken);
|
const client = new GitLabClient(instance.url, accessToken);
|
||||||
@ -365,22 +365,22 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
log.error("Gitlab auth error:", ex);
|
log.error("Gitlab auth error:", ex);
|
||||||
return this.sendNotice("Could not authenticate with GitLab. Is your token correct?");
|
return this.sendNotice("Could not authenticate with GitLab. Is your token correct?");
|
||||||
}
|
}
|
||||||
await this.sendNotice(`Connected as ${me.username}. Token stored`);
|
await this.sendNotice(`Connected as ${me.username}. Token stored.`);
|
||||||
return this.tokenStore.storeUserToken("gitlab", this.userId, accessToken, instance.url);
|
return this.tokenStore.storeUserToken("gitlab", this.userId, accessToken, instance.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@botCommand("gitlab hastoken", {help: "Check if you have a token stored for GitLab", requiredArgs: ["instanceName"], category: "gitlab"})
|
@botCommand("gitlab hastoken", {help: "Check if you have a token stored for GitLab", requiredArgs: ["instanceName"], category: "gitlab"})
|
||||||
public async gitlabHasPersonalToken(instanceName: string) {
|
public async gitlabHasPersonalToken(instanceName: string) {
|
||||||
if (!this.config.gitlab) {
|
if (!this.config.gitlab) {
|
||||||
return this.sendNotice("The bridge is not configured with GitLab support");
|
return this.sendNotice("The bridge is not configured with GitLab support.");
|
||||||
}
|
}
|
||||||
const instance = this.config.gitlab.instances[instanceName];
|
const instance = this.config.gitlab.instances[instanceName];
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
return this.sendNotice("The bridge is not configured for this GitLab instance");
|
return this.sendNotice("The bridge is not configured for this GitLab instance.");
|
||||||
}
|
}
|
||||||
const result = await this.tokenStore.getUserToken("gitlab", this.userId, instance.url);
|
const result = await this.tokenStore.getUserToken("gitlab", this.userId, instance.url);
|
||||||
if (result === null) {
|
if (result === null) {
|
||||||
return this.sendNotice("You do not currently have a token stored");
|
return this.sendNotice("You do not currently have a token stored.");
|
||||||
}
|
}
|
||||||
return this.sendNotice("A token is stored for your GitLab account.");
|
return this.sendNotice("A token is stored for your GitLab account.");
|
||||||
}
|
}
|
||||||
@ -388,7 +388,7 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
@botCommand("filters list", "List your saved filters")
|
@botCommand("filters list", "List your saved filters")
|
||||||
public async getFilters() {
|
public async getFilters() {
|
||||||
if (this.notifFilter.empty) {
|
if (this.notifFilter.empty) {
|
||||||
return this.sendNotice("You do not currently have any filters");
|
return this.sendNotice("You do not currently have any filters.");
|
||||||
}
|
}
|
||||||
const filterText = Object.entries(this.notifFilter.filters).map(([name, value]) => {
|
const filterText = Object.entries(this.notifFilter.filters).map(([name, value]) => {
|
||||||
const userText = value.users.length ? `users: ${value.users.join("|")}` : '';
|
const userText = value.users.length ? `users: ${value.users.join("|")}` : '';
|
||||||
@ -407,7 +407,7 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
const users = parameters.filter(param => param.toLowerCase().startsWith("users:")).map(param => param.toLowerCase().substring("users:".length).split(",")).flat();
|
const users = parameters.filter(param => param.toLowerCase().startsWith("users:")).map(param => param.toLowerCase().substring("users:".length).split(",")).flat();
|
||||||
const repos = parameters.filter(param => param.toLowerCase().startsWith("repos:")).map(param => param.toLowerCase().substring("repos:".length).split(",")).flat();
|
const repos = parameters.filter(param => param.toLowerCase().startsWith("repos:")).map(param => param.toLowerCase().substring("repos:".length).split(",")).flat();
|
||||||
if (orgs.length + users.length + repos.length === 0) {
|
if (orgs.length + users.length + repos.length === 0) {
|
||||||
return this.sendNotice("You must specify some filter options like 'orgs:matrix-org,half-shot', 'users:Half-Shot' or 'repos:matrix-hookshot'");
|
return this.sendNotice("You must specify some filter options like 'orgs:matrix-org,half-shot', 'users:Half-Shot' or 'repos:matrix-hookshot'.");
|
||||||
}
|
}
|
||||||
this.notifFilter.setFilter(name, {
|
this.notifFilter.setFilter(name, {
|
||||||
orgs,
|
orgs,
|
||||||
@ -415,20 +415,20 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
repos,
|
repos,
|
||||||
});
|
});
|
||||||
await this.botIntent.underlyingClient.sendStateEvent(this.roomId, NotifFilter.StateType, "", this.notifFilter.getStateContent());
|
await this.botIntent.underlyingClient.sendStateEvent(this.roomId, NotifFilter.StateType, "", this.notifFilter.getStateContent());
|
||||||
return this.sendNotice(`Stored new filter "${name}". You can now apply the filter by saying 'filters notifications toggle $name'`);
|
return this.sendNotice(`Stored new filter "${name}". You can now apply the filter by saying 'filters notifications toggle $name'.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@botCommand("filters notifications toggle", "Apply a filter as a whitelist to your notifications", ["name"])
|
@botCommand("filters notifications toggle", "Apply a filter as a whitelist to your notifications", ["name"])
|
||||||
public async setFiltersNotificationsToggle(name: string) {
|
public async setFiltersNotificationsToggle(name: string) {
|
||||||
if (!this.notifFilter.filters[name]) {
|
if (!this.notifFilter.filters[name]) {
|
||||||
return this.sendNotice(`Filter "${name}" doesn't exist'`);
|
return this.sendNotice(`Filter "${name}" doesn't exist.`);
|
||||||
}
|
}
|
||||||
if (this.notifFilter.forNotifications.has(name)) {
|
if (this.notifFilter.forNotifications.has(name)) {
|
||||||
this.notifFilter.forNotifications.delete(name);
|
this.notifFilter.forNotifications.delete(name);
|
||||||
await this.sendNotice(`Filter "${name}" disabled for notifications`);
|
await this.sendNotice(`Filter "${name}" disabled for notifications.`);
|
||||||
} else {
|
} else {
|
||||||
this.notifFilter.forNotifications.add(name);
|
this.notifFilter.forNotifications.add(name);
|
||||||
await this.sendNotice(`Filter "${name}" enabled for notifications`);
|
await this.sendNotice(`Filter "${name}" enabled for notifications.`);
|
||||||
}
|
}
|
||||||
return this.botIntent.underlyingClient.sendStateEvent(this.roomId, NotifFilter.StateType, "", this.notifFilter.getStateContent());
|
return this.botIntent.underlyingClient.sendStateEvent(this.roomId, NotifFilter.StateType, "", this.notifFilter.getStateContent());
|
||||||
}
|
}
|
||||||
@ -449,7 +449,7 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
const checkPermission = (service: string, level: BridgePermissionLevel) => this.config.checkPermission(this.userId, service, level);
|
const checkPermission = (service: string, level: BridgePermissionLevel) => this.config.checkPermission(this.userId, service, level);
|
||||||
const result = await handleCommand(this.userId, command, AdminRoom.botCommands, this, checkPermission);
|
const result = await handleCommand(this.userId, command, AdminRoom.botCommands, this, checkPermission);
|
||||||
if (!result.handled) {
|
if (!result.handled) {
|
||||||
return this.sendNotice("Command not understood");
|
return this.sendNotice("Command not understood.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("error" in result) {
|
if ("error" in result) {
|
||||||
@ -488,7 +488,7 @@ export class AdminRoom extends AdminRoomCommandHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setupWidget() {
|
public async setUpWidget() {
|
||||||
try {
|
try {
|
||||||
const res = await this.botIntent.underlyingClient.getRoomStateEvent(this.roomId, "im.vector.modular.widgets", "bridge_control");
|
const res = await this.botIntent.underlyingClient.getRoomStateEvent(this.roomId, "im.vector.modular.widgets", "bridge_control");
|
||||||
if (res) {
|
if (res) {
|
||||||
|
@ -106,10 +106,10 @@ export async function handleCommand(
|
|||||||
if (command) {
|
if (command) {
|
||||||
const permissionService = command.permissionService || defaultPermissionService;
|
const permissionService = command.permissionService || defaultPermissionService;
|
||||||
if (permissionService && !permissionCheckFn(permissionService, command.permissionLevel || BridgePermissionLevel.commands)) {
|
if (permissionService && !permissionCheckFn(permissionService, command.permissionLevel || BridgePermissionLevel.commands)) {
|
||||||
return {handled: true, error: "You do not have permission to use this command"};
|
return {handled: true, error: "You do not have permission to use this command."};
|
||||||
}
|
}
|
||||||
if (command.requiredArgs && command.requiredArgs.length > parts.length - i) {
|
if (command.requiredArgs && command.requiredArgs.length > parts.length - i) {
|
||||||
return {handled: true, error: "Missing args"};
|
return {handled: true, error: "Missing at least one required parameter."};
|
||||||
}
|
}
|
||||||
const args = parts.slice(i);
|
const args = parts.slice(i);
|
||||||
if (command.includeUserId) {
|
if (command.includeUserId) {
|
||||||
|
@ -109,7 +109,7 @@ export class Bridge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.config.figma) {
|
if (this.config.figma) {
|
||||||
// Ensure webhooks are setup
|
// Ensure webhooks are set up
|
||||||
await ensureFigmaWebhooks(this.config.figma, this.as.botClient);
|
await ensureFigmaWebhooks(this.config.figma, this.as.botClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -621,12 +621,12 @@ export class Bridge {
|
|||||||
// No state yet
|
// No state yet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const adminRoom = await this.setupAdminRoom(roomId, accountData, notifContent || NotifFilter.getDefaultContent());
|
const adminRoom = await this.setUpAdminRoom(roomId, accountData, notifContent || NotifFilter.getDefaultContent());
|
||||||
// Call this on startup to set the state
|
// Call this on startup to set the state
|
||||||
await this.onAdminRoomSettingsChanged(adminRoom, accountData, { admin_user: accountData.admin_user });
|
await this.onAdminRoomSettingsChanged(adminRoom, accountData, { admin_user: accountData.admin_user });
|
||||||
log.debug(`Room ${roomId} is connected to: ${adminRoom.toString()}`);
|
log.debug(`Room ${roomId} is connected to: ${adminRoom.toString()}`);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
log.error(`Failed to setup admin room ${roomId}:`, ex);
|
log.error(`Failed to set up admin room ${roomId}:`, ex);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -678,7 +678,7 @@ export class Bridge {
|
|||||||
}
|
}
|
||||||
await retry(() => this.as.botIntent.joinRoom(roomId), 5);
|
await retry(() => this.as.botIntent.joinRoom(roomId), 5);
|
||||||
if (event.content.is_direct) {
|
if (event.content.is_direct) {
|
||||||
const room = await this.setupAdminRoom(roomId, {admin_user: event.sender}, NotifFilter.getDefaultContent());
|
const room = await this.setUpAdminRoom(roomId, {admin_user: event.sender}, NotifFilter.getDefaultContent());
|
||||||
await this.as.botIntent.underlyingClient.setRoomAccountData(
|
await this.as.botIntent.underlyingClient.setRoomAccountData(
|
||||||
BRIDGE_ROOM_TYPE, roomId, room.accountData,
|
BRIDGE_ROOM_TYPE, roomId, room.accountData,
|
||||||
);
|
);
|
||||||
@ -1016,10 +1016,10 @@ export class Bridge {
|
|||||||
is_direct: true,
|
is_direct: true,
|
||||||
preset: "trusted_private_chat",
|
preset: "trusted_private_chat",
|
||||||
});
|
});
|
||||||
return this.setupAdminRoom(roomId, {admin_user: userId}, NotifFilter.getDefaultContent());
|
return this.setUpAdminRoom(roomId, {admin_user: userId}, NotifFilter.getDefaultContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setupAdminRoom(roomId: string, accountData: AdminAccountData, notifContent: NotificationFilterStateContent) {
|
private async setUpAdminRoom(roomId: string, accountData: AdminAccountData, notifContent: NotificationFilterStateContent) {
|
||||||
const adminRoom = new AdminRoom(
|
const adminRoom = new AdminRoom(
|
||||||
roomId, accountData, notifContent, this.as.botIntent, this.tokenStore, this.config,
|
roomId, accountData, notifContent, this.as.botIntent, this.tokenStore, this.config,
|
||||||
);
|
);
|
||||||
@ -1053,9 +1053,9 @@ export class Bridge {
|
|||||||
});
|
});
|
||||||
this.adminRooms.set(roomId, adminRoom);
|
this.adminRooms.set(roomId, adminRoom);
|
||||||
if (this.config.widgets?.addToAdminRooms && this.config.widgets.publicUrl) {
|
if (this.config.widgets?.addToAdminRooms && this.config.widgets.publicUrl) {
|
||||||
await adminRoom.setupWidget();
|
await adminRoom.setUpWidget();
|
||||||
}
|
}
|
||||||
log.debug(`Setup ${roomId} as an admin room for ${adminRoom.userId}`);
|
log.debug(`Set up ${roomId} as an admin room for ${adminRoom.userId}`);
|
||||||
return adminRoom;
|
return adminRoom;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -48,7 +48,7 @@ export abstract class CommandConnection extends BaseConnection {
|
|||||||
});
|
});
|
||||||
await this.botClient.sendEvent(this.roomId, 'm.room.message', {
|
await this.botClient.sendEvent(this.roomId, 'm.room.message', {
|
||||||
msgtype: "m.notice",
|
msgtype: "m.notice",
|
||||||
body: humanError ? `Failed to handle command: ${humanError}` : "Failed to handle command",
|
body: humanError ? `Failed to handle command: ${humanError}` : "Failed to handle command.",
|
||||||
});
|
});
|
||||||
log.warn(`Failed to handle command:`, error);
|
log.warn(`Failed to handle command:`, error);
|
||||||
return true;
|
return true;
|
||||||
|
@ -514,7 +514,7 @@ export class GitHubRepoConnection extends CommandConnection implements IConnecti
|
|||||||
const workflow = workflows.data.workflows.find(w => w.name.toLowerCase().trim() === name.toLowerCase().trim());
|
const workflow = workflows.data.workflows.find(w => w.name.toLowerCase().trim() === name.toLowerCase().trim());
|
||||||
if (!workflow) {
|
if (!workflow) {
|
||||||
const workflowNames = workflows.data.workflows.map(w => w.name).join(', ');
|
const workflowNames = workflows.data.workflows.map(w => w.name).join(', ');
|
||||||
await this.as.botIntent.sendText(this.roomId, `Could not find a workflow by the name of "${name}". The workflows on this repository are ${workflowNames}`, "m.notice");
|
await this.as.botIntent.sendText(this.roomId, `Could not find a workflow by the name of "${name}". The workflows on this repository are ${workflowNames}.`, "m.notice");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -544,7 +544,7 @@ export class GitHubRepoConnection extends CommandConnection implements IConnecti
|
|||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.as.botIntent.sendText(this.roomId, `Workflow started`, "m.notice");
|
await this.as.botIntent.sendText(this.roomId, `Workflow started.`, "m.notice");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onIssueCreated(event: IssuesOpenedEvent) {
|
public async onIssueCreated(event: IssuesOpenedEvent) {
|
||||||
|
@ -76,7 +76,7 @@ export class GitLabRepoConnection extends CommandConnection {
|
|||||||
public async onCreateIssue(userId: string, title: string, description?: string, labels?: string) {
|
public async onCreateIssue(userId: string, title: string, description?: string, labels?: string) {
|
||||||
const client = await this.tokenStore.getGitLabForUser(userId, this.instance.url);
|
const client = await this.tokenStore.getGitLabForUser(userId, this.instance.url);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
await this.as.botIntent.sendText(this.roomId, "You must login to create an issue", "m.notice");
|
await this.as.botIntent.sendText(this.roomId, "You must be logged in to create an issue.", "m.notice");
|
||||||
throw Error('Not logged in');
|
throw Error('Not logged in');
|
||||||
}
|
}
|
||||||
const res = await client.issues.create({
|
const res = await client.issues.create({
|
||||||
@ -99,7 +99,7 @@ export class GitLabRepoConnection extends CommandConnection {
|
|||||||
public async onClose(userId: string, number: string) {
|
public async onClose(userId: string, number: string) {
|
||||||
const client = await this.tokenStore.getGitLabForUser(userId, this.instance.url);
|
const client = await this.tokenStore.getGitLabForUser(userId, this.instance.url);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
await this.as.botIntent.sendText(this.roomId, "You must login to create an issue", "m.notice");
|
await this.as.botIntent.sendText(this.roomId, "You must be logged in to create an issue.", "m.notice");
|
||||||
throw Error('Not logged in');
|
throw Error('Not logged in');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ export interface IConnection {
|
|||||||
*/
|
*/
|
||||||
get connectionId(): string;
|
get connectionId(): string;
|
||||||
/**
|
/**
|
||||||
* When a room gets an update to it's state.
|
* When a room gets an update to its state.
|
||||||
*/
|
*/
|
||||||
onStateUpdate?: (ev: MatrixEvent<unknown>) => Promise<void>;
|
onStateUpdate?: (ev: MatrixEvent<unknown>) => Promise<void>;
|
||||||
/**
|
/**
|
||||||
|
@ -41,19 +41,19 @@ export class SetupConnection extends CommandConnection {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@botCommand("github repo", "Create a connection for a GitHub repository. (You must be logged in with GitHub to do this)", ["url"], [], true)
|
@botCommand("github repo", "Create a connection for a GitHub repository. (You must be logged in with GitHub to do this.)", ["url"], [], true)
|
||||||
public async onGitHubRepo(userId: string, url: string) {
|
public async onGitHubRepo(userId: string, url: string) {
|
||||||
if (!this.githubInstance || !this.config.github) {
|
if (!this.githubInstance || !this.config.github) {
|
||||||
throw new CommandError("not-configured", "The bridge is not configured to support GitHub");
|
throw new CommandError("not-configured", "The bridge is not configured to support GitHub.");
|
||||||
}
|
}
|
||||||
if (!this.config.checkPermission(userId, "github", BridgePermissionLevel.manageConnections)) {
|
if (!this.config.checkPermission(userId, "github", BridgePermissionLevel.manageConnections)) {
|
||||||
throw new CommandError('You are not permitted to provision connections for GitHub');
|
throw new CommandError('You are not permitted to provision connections for GitHub.');
|
||||||
}
|
}
|
||||||
if (!await this.as.botClient.userHasPowerLevelFor(userId, this.roomId, "", true)) {
|
if (!await this.as.botClient.userHasPowerLevelFor(userId, this.roomId, "", true)) {
|
||||||
throw new CommandError("not-configured", "You must be able to set state in a room ('Change settings') in order to setup new integrations.");
|
throw new CommandError("not-configured", "You must be able to set state in a room ('Change settings') in order to set up new integrations.");
|
||||||
}
|
}
|
||||||
if (!await this.as.botClient.userHasPowerLevelFor(this.as.botUserId, this.roomId, GitHubRepoConnection.CanonicalEventType, true)) {
|
if (!await this.as.botClient.userHasPowerLevelFor(this.as.botUserId, this.roomId, GitHubRepoConnection.CanonicalEventType, true)) {
|
||||||
throw new CommandError("Bot lacks power level to set room state", "I do not have permission to setup a bridge in this room. Please promote me to an Admin/Moderator");
|
throw new CommandError("Bot lacks power level to set room state", "I do not have permission to set up a bridge in this room. Please promote me to an Admin/Moderator.");
|
||||||
}
|
}
|
||||||
const octokit = await this.tokenStore.getOctokitForUser(userId);
|
const octokit = await this.tokenStore.getOctokitForUser(userId);
|
||||||
if (!octokit) {
|
if (!octokit) {
|
||||||
@ -61,7 +61,7 @@ export class SetupConnection extends CommandConnection {
|
|||||||
}
|
}
|
||||||
const urlParts = /^https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)$/.exec(url.trim().toLowerCase());
|
const urlParts = /^https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)$/.exec(url.trim().toLowerCase());
|
||||||
if (!urlParts) {
|
if (!urlParts) {
|
||||||
throw new CommandError("Invalid GitHub url", "The GitHub url you entered was not valid");
|
throw new CommandError("Invalid GitHub url", "The GitHub url you entered was not valid.");
|
||||||
}
|
}
|
||||||
const [, org, repo] = urlParts;
|
const [, org, repo] = urlParts;
|
||||||
const res = await GitHubRepoConnection.provisionConnection(this.roomId, userId, {org, repo}, this.as, this.tokenStore, this.githubInstance, this.config.github);
|
const res = await GitHubRepoConnection.provisionConnection(this.roomId, userId, {org, repo}, this.as, this.tokenStore, this.githubInstance, this.config.github);
|
||||||
@ -69,20 +69,20 @@ export class SetupConnection extends CommandConnection {
|
|||||||
await this.as.botClient.sendNotice(this.roomId, `Room configured to bridge ${org}/${repo}`);
|
await this.as.botClient.sendNotice(this.roomId, `Room configured to bridge ${org}/${repo}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@botCommand("jira project", "Create a connection for a JIRA project. (You must be logged in with JIRA to do this)", ["url"], [], true)
|
@botCommand("jira project", "Create a connection for a JIRA project. (You must be logged in with JIRA to do this.)", ["url"], [], true)
|
||||||
public async onJiraProject(userId: string, urlStr: string) {
|
public async onJiraProject(userId: string, urlStr: string) {
|
||||||
const url = new URL(urlStr);
|
const url = new URL(urlStr);
|
||||||
if (!this.config.jira) {
|
if (!this.config.jira) {
|
||||||
throw new CommandError("not-configured", "The bridge is not configured to support Jira");
|
throw new CommandError("not-configured", "The bridge is not configured to support Jira.");
|
||||||
}
|
}
|
||||||
if (!this.config.checkPermission(userId, "jira", BridgePermissionLevel.manageConnections)) {
|
if (!this.config.checkPermission(userId, "jira", BridgePermissionLevel.manageConnections)) {
|
||||||
throw new CommandError('You are not permitted to provision connections for Jira');
|
throw new CommandError('You are not permitted to provision connections for Jira.');
|
||||||
}
|
}
|
||||||
if (!await this.as.botClient.userHasPowerLevelFor(userId, this.roomId, "", true)) {
|
if (!await this.as.botClient.userHasPowerLevelFor(userId, this.roomId, "", true)) {
|
||||||
throw new CommandError("not-configured", "You must be able to set state in a room ('Change settings') in order to setup new integrations.");
|
throw new CommandError("not-configured", "You must be able to set state in a room ('Change settings') in order to set up new integrations.");
|
||||||
}
|
}
|
||||||
if (!await this.as.botClient.userHasPowerLevelFor(this.as.botUserId, this.roomId, GitHubRepoConnection.CanonicalEventType, true)) {
|
if (!await this.as.botClient.userHasPowerLevelFor(this.as.botUserId, this.roomId, GitHubRepoConnection.CanonicalEventType, true)) {
|
||||||
throw new CommandError("Bot lacks power level to set room state", "I do not have permission to setup a bridge in this room. Please promote me to an Admin/Moderator");
|
throw new CommandError("Bot lacks power level to set room state", "I do not have permission to set up a bridge in this room. Please promote me to an Admin/Moderator.");
|
||||||
}
|
}
|
||||||
const jiraClient = await this.tokenStore.getJiraForUser(userId, urlStr);
|
const jiraClient = await this.tokenStore.getJiraForUser(userId, urlStr);
|
||||||
if (!jiraClient) {
|
if (!jiraClient) {
|
||||||
@ -91,57 +91,57 @@ export class SetupConnection extends CommandConnection {
|
|||||||
const urlParts = /.+\/projects\/(\w+)\/?(\w+\/?)*$/.exec(url.pathname.toLowerCase());
|
const urlParts = /.+\/projects\/(\w+)\/?(\w+\/?)*$/.exec(url.pathname.toLowerCase());
|
||||||
const projectKey = urlParts?.[1] || url.searchParams.get('projectKey');
|
const projectKey = urlParts?.[1] || url.searchParams.get('projectKey');
|
||||||
if (!projectKey) {
|
if (!projectKey) {
|
||||||
throw new CommandError("Invalid Jira url", "The JIRA project url you entered was not valid. It should be in the format of `https://jira-instance/.../projects/PROJECTKEY/...` or `.../RapidBoard.jspa?projectKey=TEST`");
|
throw new CommandError("Invalid Jira url", "The JIRA project url you entered was not valid. It should be in the format of `https://jira-instance/.../projects/PROJECTKEY/...` or `.../RapidBoard.jspa?projectKey=TEST`.");
|
||||||
}
|
}
|
||||||
const safeUrl = `https://${url.host}/projects/${projectKey}`;
|
const safeUrl = `https://${url.host}/projects/${projectKey}`;
|
||||||
const res = await JiraProjectConnection.provisionConnection(this.roomId, userId, { url: safeUrl }, this.as, this.tokenStore);
|
const res = await JiraProjectConnection.provisionConnection(this.roomId, userId, { url: safeUrl }, this.as, this.tokenStore);
|
||||||
await this.as.botClient.sendStateEvent(this.roomId, JiraProjectConnection.CanonicalEventType, safeUrl, res.stateEventContent);
|
await this.as.botClient.sendStateEvent(this.roomId, JiraProjectConnection.CanonicalEventType, safeUrl, res.stateEventContent);
|
||||||
await this.as.botClient.sendNotice(this.roomId, `Room configured to bridge Jira project ${res.connection.projectKey}`);
|
await this.as.botClient.sendNotice(this.roomId, `Room configured to bridge Jira project ${res.connection.projectKey}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@botCommand("webhook", "Create an inbound webhook", ["name"], [], true)
|
@botCommand("webhook", "Create an inbound webhook.", ["name"], [], true)
|
||||||
public async onWebhook(userId: string, name: string) {
|
public async onWebhook(userId: string, name: string) {
|
||||||
if (!this.config.generic?.enabled) {
|
if (!this.config.generic?.enabled) {
|
||||||
throw new CommandError("not-configured", "The bridge is not configured to support webhooks");
|
throw new CommandError("not-configured", "The bridge is not configured to support webhooks.");
|
||||||
}
|
}
|
||||||
if (!this.config.checkPermission(userId, "webhooks", BridgePermissionLevel.manageConnections)) {
|
if (!this.config.checkPermission(userId, "webhooks", BridgePermissionLevel.manageConnections)) {
|
||||||
throw new CommandError('You are not permitted to provision connections for generic webhooks');
|
throw new CommandError('You are not permitted to provision connections for generic webhooks.');
|
||||||
}
|
}
|
||||||
if (!await this.as.botClient.userHasPowerLevelFor(userId, this.roomId, "", true)) {
|
if (!await this.as.botClient.userHasPowerLevelFor(userId, this.roomId, "", true)) {
|
||||||
throw new CommandError("not-configured", "You must be able to set state in a room ('Change settings') in order to setup new integrations.");
|
throw new CommandError("not-configured", "You must be able to set state in a room ('Change settings') in order to set up new integrations.");
|
||||||
}
|
}
|
||||||
if (!await this.as.botClient.userHasPowerLevelFor(this.as.botUserId, this.roomId, GitHubRepoConnection.CanonicalEventType, true)) {
|
if (!await this.as.botClient.userHasPowerLevelFor(this.as.botUserId, this.roomId, GitHubRepoConnection.CanonicalEventType, true)) {
|
||||||
throw new CommandError("Bot lacks power level to set room state", "I do not have permission to setup a bridge in this room. Please promote me to an Admin/Moderator");
|
throw new CommandError("Bot lacks power level to set room state", "I do not have permission to set up a bridge in this room. Please promote me to an Admin/Moderator.");
|
||||||
}
|
}
|
||||||
if (!name || name.length < 3 || name.length > 64) {
|
if (!name || name.length < 3 || name.length > 64) {
|
||||||
throw new CommandError("Bad webhook name", "A webhook name must be between 3-64 characters");
|
throw new CommandError("Bad webhook name", "A webhook name must be between 3-64 characters.");
|
||||||
}
|
}
|
||||||
const hookId = uuid();
|
const hookId = uuid();
|
||||||
const url = `${this.config.generic.urlPrefix}${this.config.generic.urlPrefix.endsWith('/') ? '' : '/'}${hookId}`;
|
const url = `${this.config.generic.urlPrefix}${this.config.generic.urlPrefix.endsWith('/') ? '' : '/'}${hookId}`;
|
||||||
await GenericHookConnection.ensureRoomAccountData(this.roomId, this.as, hookId, name);
|
await GenericHookConnection.ensureRoomAccountData(this.roomId, this.as, hookId, name);
|
||||||
await this.as.botClient.sendStateEvent(this.roomId, GenericHookConnection.CanonicalEventType, name, {hookId, name});
|
await this.as.botClient.sendStateEvent(this.roomId, GenericHookConnection.CanonicalEventType, name, {hookId, name});
|
||||||
const adminRoom = await this.getOrCreateAdminRoom(userId);
|
const adminRoom = await this.getOrCreateAdminRoom(userId);
|
||||||
await adminRoom.sendNotice(`You have bridged a webhook. Please configure your webhook source to use \`${url}\``);
|
await adminRoom.sendNotice(`You have bridged a webhook. Please configure your webhook source to use \`${url}\`.`);
|
||||||
return this.as.botClient.sendHtmlNotice(this.roomId, md.renderInline(`Room configured to bridge webhooks. See admin room for secret url.`));
|
return this.as.botClient.sendHtmlNotice(this.roomId, md.renderInline(`Room configured to bridge webhooks. See admin room for secret url.`));
|
||||||
}
|
}
|
||||||
|
|
||||||
@botCommand("figma file", "Bridge a Figma file to the room", ["url"], [], true)
|
@botCommand("figma file", "Bridge a Figma file to the room.", ["url"], [], true)
|
||||||
public async onFigma(userId: string, url: string) {
|
public async onFigma(userId: string, url: string) {
|
||||||
if (!this.config.figma) {
|
if (!this.config.figma) {
|
||||||
throw new CommandError("not-configured", "The bridge is not configured to support Figma");
|
throw new CommandError("not-configured", "The bridge is not configured to support Figma.");
|
||||||
}
|
}
|
||||||
if (!this.config.checkPermission(userId, "figma", BridgePermissionLevel.manageConnections)) {
|
if (!this.config.checkPermission(userId, "figma", BridgePermissionLevel.manageConnections)) {
|
||||||
throw new CommandError('You are not permitted to provision connections for Figma');
|
throw new CommandError('You are not permitted to provision connections for Figma.');
|
||||||
}
|
}
|
||||||
if (!await this.as.botClient.userHasPowerLevelFor(userId, this.roomId, "", true)) {
|
if (!await this.as.botClient.userHasPowerLevelFor(userId, this.roomId, "", true)) {
|
||||||
throw new CommandError("not-configured", "You must be able to set state in a room ('Change settings') in order to setup new integrations.");
|
throw new CommandError("not-configured", "You must be able to set state in a room ('Change settings') in order to set up new integrations.");
|
||||||
}
|
}
|
||||||
if (!await this.as.botClient.userHasPowerLevelFor(this.as.botUserId, this.roomId, GitHubRepoConnection.CanonicalEventType, true)) {
|
if (!await this.as.botClient.userHasPowerLevelFor(this.as.botUserId, this.roomId, GitHubRepoConnection.CanonicalEventType, true)) {
|
||||||
throw new CommandError("Bot lacks power level to set room state", "I do not have permission to setup a bridge in this room. Please promote me to an Admin/Moderator");
|
throw new CommandError("Bot lacks power level to set room state", "I do not have permission to set up a bridge in this room. Please promote me to an Admin/Moderator.");
|
||||||
}
|
}
|
||||||
const res = /https:\/\/www\.figma\.com\/file\/(\w+).+/.exec(url);
|
const res = /https:\/\/www\.figma\.com\/file\/(\w+).+/.exec(url);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
throw new CommandError("Invalid Figma url", "The Figma file url you entered was not valid. It should be in the format of `https://figma.com/file/FILEID/...`");
|
throw new CommandError("Invalid Figma url", "The Figma file url you entered was not valid. It should be in the format of `https://figma.com/file/FILEID/...`.");
|
||||||
}
|
}
|
||||||
const [, fileId] = res;
|
const [, fileId] = res;
|
||||||
await this.as.botClient.sendStateEvent(this.roomId, FigmaFileConnection.CanonicalEventType, fileId, {fileId});
|
await this.as.botClient.sendStateEvent(this.roomId, FigmaFileConnection.CanonicalEventType, fileId, {fileId});
|
||||||
|
@ -20,22 +20,22 @@ export function generateGitHubOAuthUrl(clientId: string, redirectUri: string, st
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class GitHubBotCommands extends AdminRoomCommandHandler {
|
export class GitHubBotCommands extends AdminRoomCommandHandler {
|
||||||
@botCommand("github login", {help: "Login to GitHub", category: "github"})
|
@botCommand("github login", {help: "Log in to GitHub", category: "github"})
|
||||||
public async loginCommand() {
|
public async loginCommand() {
|
||||||
if (!this.config.github) {
|
if (!this.config.github) {
|
||||||
throw new CommandError("no-github-support", "The bridge is not configured with GitHub support");
|
throw new CommandError("no-github-support", "The bridge is not configured with GitHub support.");
|
||||||
}
|
}
|
||||||
if (!this.config.github.oauth) {
|
if (!this.config.github.oauth) {
|
||||||
throw new CommandError("no-github-support", "The bridge is not configured with GitHub OAuth support");
|
throw new CommandError("no-github-support", "The bridge is not configured with GitHub OAuth support.");
|
||||||
}
|
}
|
||||||
const state = this.tokenStore.createStateForOAuth(this.userId);
|
const state = this.tokenStore.createStateForOAuth(this.userId);
|
||||||
return this.sendNotice(`To login, open ${generateGitHubOAuthUrl(this.config.github.oauth.client_id, this.config.github.oauth.redirect_uri, state)} to link your account to the bridge`);
|
return this.sendNotice(`Open ${generateGitHubOAuthUrl(this.config.github.oauth.client_id, this.config.github.oauth.redirect_uri, state)} to link your account to the bridge.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@botCommand("github setpersonaltoken", {help: "Set your personal access token for GitHub", requiredArgs: ['accessToken'], category: "github"})
|
@botCommand("github setpersonaltoken", {help: "Set your personal access token for GitHub", requiredArgs: ['accessToken'], category: "github"})
|
||||||
public async setGHPersonalAccessToken(accessToken: string) {
|
public async setGHPersonalAccessToken(accessToken: string) {
|
||||||
if (!this.config.github) {
|
if (!this.config.github) {
|
||||||
throw new CommandError("no-github-support", "The bridge is not configured with GitHub support");
|
throw new CommandError("no-github-support", "The bridge is not configured with GitHub support.");
|
||||||
}
|
}
|
||||||
let me;
|
let me;
|
||||||
try {
|
try {
|
||||||
@ -46,18 +46,18 @@ export class GitHubBotCommands extends AdminRoomCommandHandler {
|
|||||||
await this.sendNotice("Could not authenticate with GitHub. Is your token correct?");
|
await this.sendNotice("Could not authenticate with GitHub. Is your token correct?");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.sendNotice(`Connected as ${me.data.login}. Token stored`);
|
await this.sendNotice(`Connected as ${me.data.login}. Token stored.`);
|
||||||
await this.tokenStore.storeUserToken("github", this.userId, JSON.stringify({access_token: accessToken, token_type: 'pat'} as GitHubOAuthToken));
|
await this.tokenStore.storeUserToken("github", this.userId, JSON.stringify({access_token: accessToken, token_type: 'pat'} as GitHubOAuthToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
@botCommand("github hastoken", {help: "Check if you have a token stored for GitHub", category: "github"})
|
@botCommand("github hastoken", {help: "Check if you have a token stored for GitHub", category: "github"})
|
||||||
public async hasPersonalToken() {
|
public async hasPersonalToken() {
|
||||||
if (!this.config.github) {
|
if (!this.config.github) {
|
||||||
throw new CommandError("no-github-support", "The bridge is not configured with GitHub support");
|
throw new CommandError("no-github-support", "The bridge is not configured with GitHub support.");
|
||||||
}
|
}
|
||||||
const result = await this.tokenStore.getUserToken("github", this.userId);
|
const result = await this.tokenStore.getUserToken("github", this.userId);
|
||||||
if (result === null) {
|
if (result === null) {
|
||||||
await this.sendNotice("You do not currently have a token stored");
|
await this.sendNotice("You do not currently have a token stored.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.sendNotice("A token is stored for your GitHub account.");
|
await this.sendNotice("A token is stored for your GitHub account.");
|
||||||
|
@ -7,22 +7,22 @@ import { CLOUD_INSTANCE } from "./Client";
|
|||||||
const log = new LogWrapper('JiraBotCommands');
|
const log = new LogWrapper('JiraBotCommands');
|
||||||
|
|
||||||
export class JiraBotCommands extends AdminRoomCommandHandler {
|
export class JiraBotCommands extends AdminRoomCommandHandler {
|
||||||
@botCommand("jira login", {help: "Login to JIRA", category: "jira"})
|
@botCommand("jira login", {help: "Log in to JIRA", category: "jira"})
|
||||||
public async loginCommand() {
|
public async loginCommand() {
|
||||||
if (!this.config.jira?.oauth || !this.tokenStore.jiraOAuth) {
|
if (!this.config.jira?.oauth || !this.tokenStore.jiraOAuth) {
|
||||||
this.sendNotice(`Bot is not configured with JIRA OAuth support`);
|
this.sendNotice(`Bot is not configured with JIRA OAuth support.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const state = this.tokenStore.createStateForOAuth(this.userId);
|
const state = this.tokenStore.createStateForOAuth(this.userId);
|
||||||
const url = await this.tokenStore.jiraOAuth?.getAuthUrl(state);
|
const url = await this.tokenStore.jiraOAuth?.getAuthUrl(state);
|
||||||
await this.sendNotice(`To login, open ${url} to link your account to the bridge`);
|
await this.sendNotice(`Open ${url} to link your account to the bridge.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@botCommand("jira logout", {help: "Clear any login information", category: "jira"})
|
@botCommand("jira logout", {help: "Clear any login information", category: "jira"})
|
||||||
public async logout() {
|
public async logout() {
|
||||||
if (!this.config.jira?.oauth || !this.tokenStore.jiraOAuth) {
|
if (!this.config.jira?.oauth || !this.tokenStore.jiraOAuth) {
|
||||||
this.sendNotice(`Bot is not configured with JIRA OAuth support`);
|
this.sendNotice(`Bot is not configured with JIRA OAuth support.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (await this.tokenStore.clearUserToken("jira", this.userId, this.config.jira.url || CLOUD_INSTANCE)) {
|
if (await this.tokenStore.clearUserToken("jira", this.userId, this.config.jira.url || CLOUD_INSTANCE)) {
|
||||||
@ -34,13 +34,13 @@ export class JiraBotCommands extends AdminRoomCommandHandler {
|
|||||||
@botCommand("jira whoami", {help: "Determine JIRA identity", category: "jira"})
|
@botCommand("jira whoami", {help: "Determine JIRA identity", category: "jira"})
|
||||||
public async whoami() {
|
public async whoami() {
|
||||||
if (!this.config.jira) {
|
if (!this.config.jira) {
|
||||||
await this.sendNotice(`Bot is not configured with JIRA OAuth support`);
|
await this.sendNotice(`Bot is not configured with JIRA OAuth support.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const client = await this.tokenStore.getJiraForUser(this.userId, this.config.jira.url);
|
const client = await this.tokenStore.getJiraForUser(this.userId, this.config.jira.url);
|
||||||
|
|
||||||
if (!client) {
|
if (!client) {
|
||||||
await this.sendNotice(`You are not logged into JIRA`);
|
await this.sendNotice(`You are not logged into JIRA.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Get all resources for user
|
// Get all resources for user
|
||||||
@ -49,10 +49,10 @@ export class JiraBotCommands extends AdminRoomCommandHandler {
|
|||||||
resources = await client.getAccessibleResources();
|
resources = await client.getAccessibleResources();
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
log.warn(`Could not request resources from JIRA API: `, ex);
|
log.warn(`Could not request resources from JIRA API: `, ex);
|
||||||
await this.sendNotice(`Could not request JIRA resources due to an error`);
|
await this.sendNotice(`Could not request JIRA resources due to an error.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let response = resources.length === 0 ? `You do not have any instances authorised with this bot` : 'You have access to the following instances:';
|
let response = resources.length === 0 ? `You do not have any instances authorised with this bot.` : 'You have access to the following instances:';
|
||||||
for (const resource of resources) {
|
for (const resource of resources) {
|
||||||
const clientForResource = await client.getClientForResource(resource);
|
const clientForResource = await client.getClientForResource(resource);
|
||||||
if (!clientForResource) {
|
if (!clientForResource) {
|
||||||
|
@ -134,7 +134,7 @@ export class JiraProvisionerRouter {
|
|||||||
|
|
||||||
private async onOAuth(req: Request<undefined, undefined, undefined, {userId: string}>, res: Response<{url: string}>) {
|
private async onOAuth(req: Request<undefined, undefined, undefined, {userId: string}>, res: Response<{url: string}>) {
|
||||||
if (!this.tokenStore.jiraOAuth) {
|
if (!this.tokenStore.jiraOAuth) {
|
||||||
throw new ApiError('JIRA OAuth is disabled', ErrCode.DisabledFeature);
|
throw new ApiError('JIRA OAuth is disabled.', ErrCode.DisabledFeature);
|
||||||
}
|
}
|
||||||
const url = await this.tokenStore.jiraOAuth.getAuthUrl(this.tokenStore.createStateForOAuth(req.query.userId));
|
const url = await this.tokenStore.jiraOAuth.getAuthUrl(this.tokenStore.createStateForOAuth(req.query.userId));
|
||||||
res.send({ url });
|
res.send({ url });
|
||||||
@ -157,7 +157,7 @@ export class JiraProvisionerRouter {
|
|||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
log.warn(`Failed to fetch accessible resources for ${req.query.userId}`, ex);
|
log.warn(`Failed to fetch accessible resources for ${req.query.userId}`, ex);
|
||||||
return next( new ApiError("Could not fetch accessible resources for JIRA user", ErrCode.Unknown));
|
return next( new ApiError("Could not fetch accessible resources for JIRA user.", ErrCode.Unknown));
|
||||||
}
|
}
|
||||||
return res.send({
|
return res.send({
|
||||||
loggedIn: true,
|
loggedIn: true,
|
||||||
@ -177,10 +177,10 @@ export class JiraProvisionerRouter {
|
|||||||
resClient = await jiraUser.getClientForName(req.params.instanceName);
|
resClient = await jiraUser.getClientForName(req.params.instanceName);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
log.warn(`Failed to fetch client for ${req.params.instanceName} for ${req.query.userId}`, ex);
|
log.warn(`Failed to fetch client for ${req.params.instanceName} for ${req.query.userId}`, ex);
|
||||||
return next( new ApiError("Could not fetch accessible resources for JIRA user", ErrCode.Unknown));
|
return next( new ApiError("Could not fetch accessible resources for JIRA user.", ErrCode.Unknown));
|
||||||
}
|
}
|
||||||
if (!resClient) {
|
if (!resClient) {
|
||||||
return next( new ApiError("Instance not known or not accessible to this user", ErrCode.ForbiddenUser));
|
return next( new ApiError("Instance not known or not accessible to this user.", ErrCode.ForbiddenUser));
|
||||||
}
|
}
|
||||||
|
|
||||||
const projects = [];
|
const projects = [];
|
||||||
@ -195,7 +195,7 @@ export class JiraProvisionerRouter {
|
|||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
log.warn(`Failed to fetch accessible projects for ${req.params.instanceName} / ${req.query.userId}`, ex);
|
log.warn(`Failed to fetch accessible projects for ${req.params.instanceName} / ${req.query.userId}`, ex);
|
||||||
return next( new ApiError("Could not fetch accessible projects for JIRA user", ErrCode.Unknown));
|
return next( new ApiError("Could not fetch accessible projects for JIRA user.", ErrCode.Unknown));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.send(projects);
|
return res.send(projects);
|
||||||
|
@ -76,7 +76,7 @@ export class UserTokenStore {
|
|||||||
|
|
||||||
public async storeUserToken(type: TokenType, userId: string, token: string, instanceUrl?: string): Promise<void> {
|
public async storeUserToken(type: TokenType, userId: string, token: string, instanceUrl?: string): Promise<void> {
|
||||||
if (!this.config.checkPermission(userId, type, BridgePermissionLevel.login)) {
|
if (!this.config.checkPermission(userId, type, BridgePermissionLevel.login)) {
|
||||||
throw new ApiError('User does not have permission to login to service', ErrCode.ForbiddenUser);
|
throw new ApiError('User does not have permission to log in to service', ErrCode.ForbiddenUser);
|
||||||
}
|
}
|
||||||
const key = tokenKey(type, userId, false, instanceUrl);
|
const key = tokenKey(type, userId, false, instanceUrl);
|
||||||
const tokenParts: string[] = [];
|
const tokenParts: string[] = [];
|
||||||
|
@ -55,7 +55,7 @@ export async function ensureFigmaWebhooks(figmaConfig: BridgeConfigFigma, matrix
|
|||||||
webhookDefinition = res.data as FigmaWebhookDefinition;
|
webhookDefinition = res.data as FigmaWebhookDefinition;
|
||||||
await matrixClient.setAccountData(accountDataKey, {webhookId: webhookDefinition.id});
|
await matrixClient.setAccountData(accountDataKey, {webhookId: webhookDefinition.id});
|
||||||
}
|
}
|
||||||
// Webhook is ready and setup
|
// Webhook is ready and set up
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -45,7 +45,7 @@ Request the connection types enabled for this bridge.
|
|||||||
"type": "JiraProject", // The name of the connection
|
"type": "JiraProject", // The name of the connection
|
||||||
"eventType": "uk.half-shot.matrix-hookshot.jira.project", // Corresponds to the state type for the connection
|
"eventType": "uk.half-shot.matrix-hookshot.jira.project", // Corresponds to the state type for the connection
|
||||||
"service": "jira", // or github, webhook. A human-readable service name to make things look pretty
|
"service": "jira", // or github, webhook. A human-readable service name to make things look pretty
|
||||||
"botUserId": "@hookshot:yourdomain.com", // The bot mxid for the service. Currently this is the sender_localpart, but may change in the future.
|
"botUserId": "@hookshot:yourdomain.com", // The bot mxid for the service. Currently, this is the sender_localpart, but may change in the future.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -62,7 +62,7 @@ Request the connections for a given room. The `{roomId}` parameter is the target
|
|||||||
"eventType": "uk.half-shot.matrix-hookshot.jira.project", // Corresponds to the state type in the connection
|
"eventType": "uk.half-shot.matrix-hookshot.jira.project", // Corresponds to the state type in the connection
|
||||||
"id": "opaque-unique-id", // An opaque ID used to refer to this connection. Should **NOT** be assumed to be stable.
|
"id": "opaque-unique-id", // An opaque ID used to refer to this connection. Should **NOT** be assumed to be stable.
|
||||||
"service": "jira", // or github, webhook. A human-readable service name to make things look pretty
|
"service": "jira", // or github, webhook. A human-readable service name to make things look pretty
|
||||||
"botUserId": "@hookshot:yourdomain.com", // The bot mxid for the service. Currently this is the sender_localpart, but may change in the future.
|
"botUserId": "@hookshot:yourdomain.com", // The bot mxid for the service. Currently, this is the sender_localpart, but may change in the future.
|
||||||
"config": {
|
"config": {
|
||||||
// ... connection specific details, can be configured.
|
// ... connection specific details, can be configured.
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ Request details of a single connection. The `{roomId}` parameter is the target M
|
|||||||
"eventType": "uk.half-shot.matrix-hookshot.jira.project", // Corresponds to the state type in the connection
|
"eventType": "uk.half-shot.matrix-hookshot.jira.project", // Corresponds to the state type in the connection
|
||||||
"id": "opaque-unique-id", // An opaque ID used to refer to this connection. Should **NOT** be assumed to be stable.
|
"id": "opaque-unique-id", // An opaque ID used to refer to this connection. Should **NOT** be assumed to be stable.
|
||||||
"service": "jira", // or github, webhook. A human-readable service name to make things look pretty
|
"service": "jira", // or github, webhook. A human-readable service name to make things look pretty
|
||||||
"botUserId": "@hookshot:yourdomain.com", // The bot mxid for the service. Currently this is the sender_localpart, but may change in the future.
|
"botUserId": "@hookshot:yourdomain.com", // The bot mxid for the service. Currently, this is the sender_localpart, but may change in the future.
|
||||||
"config": {
|
"config": {
|
||||||
// ... connection specific details, can be configured.
|
// ... connection specific details, can be configured.
|
||||||
}
|
}
|
||||||
@ -110,7 +110,7 @@ The body of the request is the configuration for the connection, which will be t
|
|||||||
"eventType": "uk.half-shot.matrix-hookshot.jira.project", // Corresponds to the state type in the connection
|
"eventType": "uk.half-shot.matrix-hookshot.jira.project", // Corresponds to the state type in the connection
|
||||||
"id": "opaque-unique-id", // An opaque ID used to refer to this connection. Should **NOT** be assumed to be stable.
|
"id": "opaque-unique-id", // An opaque ID used to refer to this connection. Should **NOT** be assumed to be stable.
|
||||||
"service": "jira", // or github, webhook. A human-readable service name to make things look pretty
|
"service": "jira", // or github, webhook. A human-readable service name to make things look pretty
|
||||||
"botUserId": "@hookshot:yourdomain.com", // The bot mxid for the service. Currently this is the sender_localpart, but may change in the future.
|
"botUserId": "@hookshot:yourdomain.com", // The bot mxid for the service. Currently, this is the sender_localpart, but may change in the future.
|
||||||
"config": {
|
"config": {
|
||||||
// ... connection specific details, can be configured.
|
// ... connection specific details, can be configured.
|
||||||
}
|
}
|
||||||
@ -138,7 +138,7 @@ The body of the request is the configuration for the connection, which will be t
|
|||||||
"eventType": "uk.half-shot.matrix-hookshot.jira.project", // Corresponds to the state type in the connection
|
"eventType": "uk.half-shot.matrix-hookshot.jira.project", // Corresponds to the state type in the connection
|
||||||
"id": "opaque-unique-id", // An opaque ID used to refer to this connection. Should **NOT** be assumed to be stable.
|
"id": "opaque-unique-id", // An opaque ID used to refer to this connection. Should **NOT** be assumed to be stable.
|
||||||
"service": "jira", // or github, webhook. A human-readable service name to make things look pretty
|
"service": "jira", // or github, webhook. A human-readable service name to make things look pretty
|
||||||
"botUserId": "@hookshot:yourdomain.com", // The bot mxid for the service. Currently this is the sender_localpart, but may change in the future.
|
"botUserId": "@hookshot:yourdomain.com", // The bot mxid for the service. Currently, this is the sender_localpart, but may change in the future.
|
||||||
"config": {
|
"config": {
|
||||||
// ... connection specific details, can be configured.
|
// ... connection specific details, can be configured.
|
||||||
}
|
}
|
||||||
|
@ -185,10 +185,10 @@ export class Provisioner {
|
|||||||
private getConnection(req: Request<{roomId: string, connectionId: string}>, res: Response<GetConnectionsResponseItem>) {
|
private getConnection(req: Request<{roomId: string, connectionId: string}>, res: Response<GetConnectionsResponseItem>) {
|
||||||
const connection = this.connMan.getConnectionById(req.params.roomId, req.params.connectionId);
|
const connection = this.connMan.getConnectionById(req.params.roomId, req.params.connectionId);
|
||||||
if (!connection) {
|
if (!connection) {
|
||||||
throw new ApiError("Connection does not exist", ErrCode.NotFound);
|
throw new ApiError("Connection does not exist.", ErrCode.NotFound);
|
||||||
}
|
}
|
||||||
if (!connection.getProvisionerDetails) {
|
if (!connection.getProvisionerDetails) {
|
||||||
throw new ApiError("Connection type does not support updates", ErrCode.UnsupportedOperation);
|
throw new ApiError("Connection type does not support updates.", ErrCode.UnsupportedOperation);
|
||||||
}
|
}
|
||||||
return res.send(connection.getProvisionerDetails());
|
return res.send(connection.getProvisionerDetails());
|
||||||
}
|
}
|
||||||
@ -197,15 +197,15 @@ export class Provisioner {
|
|||||||
// Need to figure out which connections are available
|
// Need to figure out which connections are available
|
||||||
try {
|
try {
|
||||||
if (!req.body || typeof req.body !== "object") {
|
if (!req.body || typeof req.body !== "object") {
|
||||||
throw new ApiError("A JSON body must be provided", ErrCode.BadValue);
|
throw new ApiError("A JSON body must be provided.", ErrCode.BadValue);
|
||||||
}
|
}
|
||||||
const connection = await this.connMan.provisionConnection(req.params.roomId, req.query.userId, req.params.type, req.body);
|
const connection = await this.connMan.provisionConnection(req.params.roomId, req.query.userId, req.params.type, req.body);
|
||||||
if (!connection.getProvisionerDetails) {
|
if (!connection.getProvisionerDetails) {
|
||||||
throw new Error('Connection supported provisioning but not getProvisionerDetails');
|
throw new Error('Connection supported provisioning but not getProvisionerDetails.');
|
||||||
}
|
}
|
||||||
res.send(connection.getProvisionerDetails());
|
res.send(connection.getProvisionerDetails());
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
log.warn(`Failed to create connection for ${req.params.roomId}`, ex);
|
log.warn(`Failed to create connection for ${req.params.roomId}.`, ex);
|
||||||
return next(ex);
|
return next(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,10 +214,10 @@ export class Provisioner {
|
|||||||
try {
|
try {
|
||||||
const connection = this.connMan.getConnectionById(req.params.roomId, req.params.connectionId);
|
const connection = this.connMan.getConnectionById(req.params.roomId, req.params.connectionId);
|
||||||
if (!connection) {
|
if (!connection) {
|
||||||
return next(new ApiError("Connection does not exist", ErrCode.NotFound));
|
return next(new ApiError("Connection does not exist.", ErrCode.NotFound));
|
||||||
}
|
}
|
||||||
if (!connection.provisionerUpdateConfig || !connection.getProvisionerDetails) {
|
if (!connection.provisionerUpdateConfig || !connection.getProvisionerDetails) {
|
||||||
return next(new ApiError("Connection type does not support updates", ErrCode.UnsupportedOperation));
|
return next(new ApiError("Connection type does not support updates.", ErrCode.UnsupportedOperation));
|
||||||
}
|
}
|
||||||
await connection.provisionerUpdateConfig(req.query.userId, req.body);
|
await connection.provisionerUpdateConfig(req.query.userId, req.body);
|
||||||
res.send(connection.getProvisionerDetails());
|
res.send(connection.getProvisionerDetails());
|
||||||
@ -230,10 +230,10 @@ export class Provisioner {
|
|||||||
try {
|
try {
|
||||||
const connection = this.connMan.getConnectionById(req.params.roomId, req.params.connectionId);
|
const connection = this.connMan.getConnectionById(req.params.roomId, req.params.connectionId);
|
||||||
if (!connection) {
|
if (!connection) {
|
||||||
return next(new ApiError("Connection does not exist", ErrCode.NotFound));
|
return next(new ApiError("Connection does not exist.", ErrCode.NotFound));
|
||||||
}
|
}
|
||||||
if (!connection.onRemove) {
|
if (!connection.onRemove) {
|
||||||
return next(new ApiError("Connection does not support removal", ErrCode.UnsupportedOperation));
|
return next(new ApiError("Connection does not support removal.", ErrCode.UnsupportedOperation));
|
||||||
}
|
}
|
||||||
await this.connMan.purgeConnection(req.params.roomId, req.params.connectionId);
|
await this.connMan.purgeConnection(req.params.roomId, req.params.connectionId);
|
||||||
res.send({ok: true});
|
res.send({ok: true});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user