Skip to content

Commit

Permalink
Discord Notifications (#188)
Browse files Browse the repository at this point in the history
* feat(discord): initial commit

* feat(discord): initial auth setup

* added a scope for discord to allow channel selection

* added a scope for discord to allow channel selection

* added space content updated event to discord integration

* added messages api url to discord integration

* feat(discord): get channel id via auth webhook

* added webhook to extended oauthresponse

* feat(discord): use bot token for auth

* feat(discord): post message in channel and add embedObject

* feat(discord): organise and clean up todos and comments

* Update manifest

---------

Co-authored-by: chanlevandermerwe <[email protected]>
Co-authored-by: megantipps <[email protected]>
  • Loading branch information
3 people authored Jun 26, 2023
1 parent 48957c5 commit 71cf45e
Show file tree
Hide file tree
Showing 11 changed files with 908 additions and 911 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ yarn-error.log*
# turbo
.turbo


.env

.idea

.gitbook-dev.yaml
.gitbook-dev.yaml
3 changes: 3 additions & 0 deletions integrations/discord/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["@gitbook/eslint-config/integration"]
}
Binary file added integrations/discord/assets/discord-preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added integrations/discord/assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 46 additions & 0 deletions integrations/discord/gitbook-manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: discord
title: Discord
icon: ./assets/icon.png
organization: gitbook
visibility: public
categories:
- collaboration
description: Notify a server channel in Discord with real-time events from GitBook.
summary: |
# Overview
The GitBook Discord integration brings the power of GitBook to your Discord Server. Your members have instant access to your GitBook knowledge base, without leaving the app.
# How it works
Get notified of what's important:
Receive real-time Discord notifications when something important happens to your content on GitBook. For example, when your content is updated or published.
Pick and choose what, where, and how you wish to be notified.
# Configure
You can install the integration on a single space by clicking the integrations button in sub-navigation panel. If you prefer to install the Discord integration on multiple on all spaces you can enable this through organization settings. To configure the integration you will have to authorize the connection between Discord and GitBook. You can also select the default server to post messages to.
previewImages:
- ./assets/discord-preview.png
externalLinks:
- label: Homepage
url: https://www.discord.com
script: src/index.tsx
scopes:
- space:content:read
- space:metadata:read
configurations:
account:
properties:
oauth_credentials:
type: button
title: Connection
description: Authorization between Discord and GitBook.
button_text: Authorize
callback_url: /oauth
required:
- oauth_credentials
secrets:
CLIENT_ID: ${{ env.DISCORD_CLIENT_ID }}
CLIENT_SECRET: ${{ env.DISCORD_CLIENT_SECRET }}
BOT_TOKEN: ${{ env.DISCORD_BOT_TOKEN }}
16 changes: 16 additions & 0 deletions integrations/discord/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "discord",
"private": true,
"scripts": {
"lint": "eslint ./**/*.ts*",
"typecheck": "tsc --noEmit",
"publish-integrations-staging": "gitbook publish ."
},
"devDependencies": {
"@gitbook/cli": "^0.8.0",
"@gitbook/runtime": "latest"
},
"dependencies": {
"itty-router": "^4.0.9"
}
}
38 changes: 38 additions & 0 deletions integrations/discord/src/discord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export const sendDiscordMessage = async (event, context) => {
const { environment, api } = context;
const { data: space } = await api.spaces.getSpaceById(event.spaceId);

const channelId =
environment.installation?.configuration.oauth_credentials?.webhook?.channel_id;

const url = `https://discord.com/api/v10/channels/${channelId}/messages`;

const botToken = environment.secrets.BOT_TOKEN;

const accessToken = environment.installation?.configuration.oauth_credentials?.access_token;

if (!accessToken) {
throw new Error('No authentication token provided');
}

const embedObject = {
title: 'View changes here:',
description: `${space.urls.app}`,
thumbnail: {
url: 'https://avatars.githubusercontent.com/u/7111340?s=280&v=4',
},
};

const headers = {
'Content-Type': 'application/json',
Authorization: `Bot ${botToken}`,
};
const body = JSON.stringify({
content: `Changes have been made in ${space.title}.`,
embeds: [embedObject],
});

await fetch(url, { method: 'POST', headers, body }).catch((err) => {
throw new Error(`Error fetching content from ${url}. ${err}`);
});
};
84 changes: 84 additions & 0 deletions integrations/discord/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Router } from 'itty-router';

// eslint-disable-next-line import/no-extraneous-dependencies
import { RequestUpdateIntegrationInstallation } from '@gitbook/api';
import {
createIntegration,
createOAuthHandler,
FetchEventCallback,
EventCallback,
} from '@gitbook/runtime';

import { sendDiscordMessage } from './discord';
import { DiscordRuntimeContext, OAuthResponseWebhook } from './types';

/*
* Handle content being updated: send a notification on Slack.
*/
const handleSpaceContentUpdated: EventCallback<
'space_content_updated',
DiscordRuntimeContext
> = async (event, context) => {
await sendDiscordMessage(event, context);
};

const handleFetchEvent: FetchEventCallback<DiscordRuntimeContext> = async (request, context) => {
const { environment } = context;

const router = Router({
base: new URL(
environment.spaceInstallation?.urls?.publicEndpoint ||
environment.installation?.urls.publicEndpoint ||
environment.integration.urls.publicEndpoint
).pathname,
});

/*
* Authenticate the user using OAuth.
*/
router.get(
'/oauth',
// @ts-ignore
createOAuthHandler({
redirectURL: `${context.environment.integration.urls.publicEndpoint}/oauth`,
clientId: environment.secrets.CLIENT_ID,
clientSecret: environment.secrets.CLIENT_SECRET,
authorizeURL:
'https://discord.com/api/oauth2/authorize?response_type=code&permissions=2147485696',
accessTokenURL: 'https://discord.com/api/oauth2/token',
scopes: ['applications.commands', 'bot', 'webhook.incoming'],
extractCredentials,
})
);

const response = await router.handle(request, context);
if (!response) {
return new Response(`No route matching ${request.method} ${request.url}`, {
status: 404,
});
}
return response;
};

const extractCredentials = async (
response: OAuthResponseWebhook
): Promise<RequestUpdateIntegrationInstallation> => {
const { access_token, webhook } = response;
return {
configuration: {
oauth_credentials: {
access_token,
expires_at: Date.now() + response.expires_in * 1000,
refresh_token: response.refresh_token,
webhook,
},
},
};
};

export default createIntegration<DiscordRuntimeContext>({
fetch: handleFetchEvent,
events: {
space_content_updated: handleSpaceContentUpdated,
},
});
44 changes: 44 additions & 0 deletions integrations/discord/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { RuntimeEnvironment, RuntimeContext, OAuthResponse } from '@gitbook/runtime';

export interface DiscordInstallationConfiguration {
oauth_credentials?: {
access_token: string;
webhook: {
application_id: string;
name: string;
url: string;
channel_id: string;
token: string;
type: number;
avatar?: string;
guild_id: string;
id: string;
};
};
}

export interface DiscordSpaceInstallationConfiguration {
channel?: string;
notify_content_update?: boolean;
notify_visibility_update?: boolean;
}

export type DiscordRuntimeEnvironment = RuntimeEnvironment<
DiscordInstallationConfiguration,
DiscordSpaceInstallationConfiguration
>;
export type DiscordRuntimeContext = RuntimeContext<DiscordRuntimeEnvironment>;

export interface OAuthResponseWebhook extends OAuthResponse {
webhook: {
application_id: string;
name: string;
url: string;
channel_id: string;
token: string;
type: number;
avatar?: string;
guild_id: string;
id: string;
};
}
3 changes: 3 additions & 0 deletions integrations/discord/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "@gitbook/tsconfig/integration.json"
}
Loading

0 comments on commit 71cf45e

Please sign in to comment.