├── src ├── templates │ ├── index.ts │ ├── widgetMemberList.ts │ └── widget.ts ├── interfaces │ ├── index.ts │ └── widget.ts ├── utils │ ├── index.ts │ ├── setStyle.ts │ └── fetchData.ts ├── template.ts ├── options.ts ├── widget.ts ├── index.ts └── style.ts ├── dist ├── interfaces │ ├── index.d.ts │ └── widget.d.ts ├── templates │ ├── index.d.ts │ ├── widgetMemberList.d.ts │ └── widget.d.ts ├── utils │ ├── index.d.ts │ ├── setStyle.d.ts │ └── fetchData.d.ts ├── style.d.ts ├── template.d.ts ├── options.d.ts ├── widget.d.ts ├── index.d.ts ├── discord-widget.js.LICENSE.txt └── discord-widget.js ├── .gitignore ├── .vscode └── settings.json ├── .eslintignore ├── .eslintrc ├── docs └── pull_request_template.md ├── tsconfig.json ├── SECURITY.md ├── demo └── index.html ├── webpack.dev.js ├── webpack.prod.js ├── LICENSE ├── package.json ├── webpack.config.js ├── README.md └── .github └── workflows └── codeql-analysis.yml /src/templates/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./widget"; 2 | -------------------------------------------------------------------------------- /dist/interfaces/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./widget"; 2 | -------------------------------------------------------------------------------- /dist/templates/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./widget"; 2 | -------------------------------------------------------------------------------- /src/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./widget"; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /.history 3 | /.parcel-cache 4 | /.vscode -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } -------------------------------------------------------------------------------- /dist/utils/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./fetchData"; 2 | export * from "./setStyle"; 3 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./fetchData"; 2 | export * from "./setStyle"; 3 | -------------------------------------------------------------------------------- /dist/templates/widgetMemberList.d.ts: -------------------------------------------------------------------------------- 1 | export declare const getMemeberList: (guild: any) => any; 2 | -------------------------------------------------------------------------------- /dist/utils/setStyle.d.ts: -------------------------------------------------------------------------------- 1 | export declare function setStyle(el: HTMLElement, styles: any): void; 2 | -------------------------------------------------------------------------------- /dist/style.d.ts: -------------------------------------------------------------------------------- 1 | export declare function attachStyle(): void; 2 | export declare function detachStyle(): void; 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | webpack* 4 | custom.d.ts 5 | styleMock.js 6 | jest.config.js 7 | jest-config -------------------------------------------------------------------------------- /dist/utils/fetchData.d.ts: -------------------------------------------------------------------------------- 1 | import { Guild } from "../interfaces/"; 2 | export declare function fetchData(id: string | undefined): Promise; 3 | -------------------------------------------------------------------------------- /dist/template.d.ts: -------------------------------------------------------------------------------- 1 | import { Guild } from "./interfaces/"; 2 | export declare function template(data: Guild, action: (data: Guild) => string | string[]): any; 3 | -------------------------------------------------------------------------------- /dist/templates/widget.d.ts: -------------------------------------------------------------------------------- 1 | import { WidgetOptions, Guild } from "../interfaces/"; 2 | export declare const getWidget: (guild: Guild, options: WidgetOptions) => string; 3 | -------------------------------------------------------------------------------- /src/template.ts: -------------------------------------------------------------------------------- 1 | import { Guild } from "./interfaces/"; 2 | 3 | export function template( 4 | data: Guild, 5 | action: (data: Guild) => string | string[] 6 | ): any { 7 | return action(data); 8 | } 9 | -------------------------------------------------------------------------------- /dist/options.d.ts: -------------------------------------------------------------------------------- 1 | import { WidgetOptions } from "./interfaces/"; 2 | export declare class Options { 3 | /** 4 | * 5 | */ 6 | connectButton: boolean; 7 | constructor(options?: Partial); 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/setStyle.ts: -------------------------------------------------------------------------------- 1 | export function setStyle(el: HTMLElement, styles: any) { 2 | Object.keys(styles).forEach((prop: any) => { 3 | el.style[ 4 | prop 5 | .split(/(?=[A-Z])/) 6 | .join("-") 7 | .toLowerCase() 8 | ] = styles[prop]; 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /src/options.ts: -------------------------------------------------------------------------------- 1 | import { WidgetOptions } from "./interfaces/"; 2 | 3 | export class Options { 4 | /** 5 | * 6 | */ 7 | connectButton = true; 8 | 9 | constructor(options: Partial = {}) { 10 | Object.keys(options).forEach((prop) => { 11 | this[prop] = options[prop]; 12 | }); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/fetchData.ts: -------------------------------------------------------------------------------- 1 | import { Guild } from "../interfaces/"; 2 | 3 | export async function fetchData(id: string | undefined): Promise { 4 | try { 5 | const URL = `https://discord.com/api/guilds/${id}/widget.json`; 6 | const data: Guild = await (await fetch(URL)).json(); 7 | return data; 8 | } catch (err) { 9 | throw new Error(err); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:@typescript-eslint/recommended" 9 | ], 10 | "parser": "@typescript-eslint/parser", 11 | "parserOptions": { 12 | "ecmaVersion": 12, 13 | "sourceType": "module" 14 | }, 15 | "plugins": [ 16 | "@typescript-eslint" 17 | ], 18 | "rules": { 19 | } 20 | } -------------------------------------------------------------------------------- /dist/widget.d.ts: -------------------------------------------------------------------------------- 1 | import * as I from "./interfaces/"; 2 | import { WidgetOptions, Guild } from "./interfaces/"; 3 | export declare class Widget implements I.Widget { 4 | /** 5 | * Options for current Widget instance 6 | */ 7 | readonly options: WidgetOptions | undefined; 8 | /** 9 | * The element that this widget is attached 10 | */ 11 | readonly element: HTMLElement; 12 | /** 13 | * Fetched data from discord API 14 | */ 15 | readonly guild: Guild; 16 | constructor(element: HTMLElement, guild: Guild, options?: WidgetOptions | undefined); 17 | } 18 | -------------------------------------------------------------------------------- /docs/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. 4 | 5 | Fixes # (issue) 6 | 7 | ## Type of change 8 | 9 | Please delete options that are not relevant. 10 | 11 | - [ ] Bug fix (non-breaking change which fixes an issue) 12 | - [ ] New feature (non-breaking change which adds functionality) 13 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 14 | - [ ] This change requires a documentation update 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "experimentalDecorators": true, 5 | "noUnusedParameters": true, 6 | "noUnusedLocals": true, 7 | "importHelpers": true, 8 | "declaration": true, 9 | "sourceMap": true, 10 | "jsx": "react", 11 | "module": "commonjs", 12 | "target": "es2017", 13 | "lib": ["esnext", "dom", "es6"], 14 | "outDir": "dist", 15 | "baseUrl": ".", 16 | "paths": { 17 | "discord-widget": [ "src/index" ], 18 | "discord-widget/*": [ "src/*" ] 19 | } 20 | }, 21 | "exclude": [ 22 | "node_modules", 23 | "build", 24 | "dist", 25 | "webpack" 26 | ] 27 | } -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 |
13 | 14 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /webpack.dev.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require("webpack"); 3 | const UglifyJSPlugin = require("uglifyjs-webpack-plugin"); 4 | 5 | const resolveApp = (relativePath) => path.resolve(__dirname, relativePath); 6 | 7 | module.exports = { 8 | mode: "production", 9 | entry: [resolveApp("src/index.ts")], 10 | output: { 11 | path: resolveApp("dist/"), 12 | filename: "discord-widget.js", 13 | library: "DiscordWidget", 14 | libraryTarget: "umd", 15 | libraryExport: "default", 16 | umdNamedDefine: true, 17 | globalObject: "this", 18 | }, 19 | plugins: [ 20 | new UglifyJSPlugin(), 21 | new webpack.optimize.ModuleConcatenationPlugin(), 22 | ], 23 | }; 24 | -------------------------------------------------------------------------------- /webpack.prod.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require("webpack"); 3 | const UglifyJSPlugin = require("uglifyjs-webpack-plugin"); 4 | 5 | const resolveApp = (relativePath) => path.resolve(__dirname, relativePath); 6 | 7 | module.exports = { 8 | mode: "production", 9 | entry: [resolveApp("src/index.ts")], 10 | output: { 11 | path: resolveApp("dist/"), 12 | filename: "discord-widget.js", 13 | library: "DiscordWidget", 14 | libraryTarget: "umd", 15 | libraryExport: "default", 16 | umdNamedDefine: true, 17 | globalObject: "this", 18 | }, 19 | plugins: [ 20 | new UglifyJSPlugin(), 21 | new webpack.optimize.ModuleConcatenationPlugin(), 22 | ], 23 | }; 24 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | import { WidgetOptions } from "./interfaces/"; 2 | import { Widget } from "./widget"; 3 | export default class DiscordWidget extends Widget { 4 | static version: string; 5 | /** 6 | * Initializes the widget on the specified element 7 | * 8 | * @param element The DOM element to initialize 9 | * @param guildId Guild identifier 10 | * @param options [optional] Options for the widget 11 | */ 12 | static init(element: HTMLElement | null, guildId: string, options?: WidgetOptions): Promise; 13 | /** 14 | * Attaches default styles to current document. 15 | */ 16 | static attachStyle(): void; 17 | /** 18 | * Removes default styles from current document. 19 | */ 20 | static detachStyle(): void; 21 | } 22 | -------------------------------------------------------------------------------- /dist/interfaces/widget.d.ts: -------------------------------------------------------------------------------- 1 | export declare type WidgetOptions = { 2 | connectButton?: boolean; 3 | styles?: Record; 4 | }; 5 | export declare type Guild = { 6 | id: string; 7 | name: string; 8 | instant_invite: string; 9 | channels: Channels[]; 10 | members: Members[]; 11 | presence_count: number; 12 | }; 13 | export declare type Channels = { 14 | id: string; 15 | name: string; 16 | position: number; 17 | }; 18 | export declare type Members = { 19 | id: string; 20 | username: string; 21 | discriminator: string; 22 | avatar: null; 23 | status: string; 24 | game?: { 25 | name: string; 26 | }; 27 | avatar_url: string; 28 | }; 29 | export interface Widget { 30 | readonly options: WidgetOptions | undefined; 31 | readonly guild: Guild; 32 | } 33 | -------------------------------------------------------------------------------- /src/interfaces/widget.ts: -------------------------------------------------------------------------------- 1 | export type WidgetOptions = { 2 | connectButton?: boolean; 3 | styles?: Record; 4 | }; 5 | 6 | export type Guild = { 7 | id: string; 8 | name: string; 9 | instant_invite: string; 10 | channels: Channels[]; 11 | members: Members[]; 12 | presence_count: number; 13 | }; 14 | 15 | export type Channels = { 16 | id: string; 17 | name: string; 18 | position: number; 19 | }; 20 | 21 | export type Members = { 22 | id: string; 23 | username: string; 24 | discriminator: string; 25 | avatar: null; 26 | status: string; 27 | game?: { name: string }; 28 | avatar_url: string; 29 | }; 30 | 31 | export interface Widget { 32 | readonly options: WidgetOptions | undefined; 33 | readonly guild: Guild; 34 | } 35 | -------------------------------------------------------------------------------- /dist/discord-widget.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! ***************************************************************************** 2 | Copyright (c) Microsoft Corporation. 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 12 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | PERFORMANCE OF THIS SOFTWARE. 14 | ***************************************************************************** */ 15 | -------------------------------------------------------------------------------- /src/templates/widgetMemberList.ts: -------------------------------------------------------------------------------- 1 | import { template } from "../template"; 2 | 3 | export const getMemeberList = (guild) => 4 | template(guild, (data) => 5 | data.members 6 | .map( 7 | (member) => 8 | ` 9 |
10 |
11 |
12 | 14 | 17 |
18 | ${member.username} 19 |
20 | ${ 21 | member.game === undefined 22 | ? `` 23 | : `${member.game.name}` 24 | } 25 |
26 | ` 27 | ) 28 | .join("") 29 | ); 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021-present Jakub Szołtysek 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/widget.ts: -------------------------------------------------------------------------------- 1 | import * as I from "./interfaces/"; 2 | import { WidgetOptions, Guild } from "./interfaces/"; 3 | import { getWidget } from "./templates"; 4 | import { Options } from "./options"; 5 | 6 | export class Widget implements I.Widget { 7 | /** 8 | * Options for current Widget instance 9 | */ 10 | readonly options: WidgetOptions | undefined; 11 | 12 | /** 13 | * The element that this widget is attached 14 | */ 15 | readonly element: HTMLElement; 16 | 17 | /** 18 | * Fetched data from discord API 19 | */ 20 | readonly guild: Guild; 21 | 22 | constructor( 23 | element: HTMLElement, 24 | guild: Guild, 25 | options?: WidgetOptions | undefined 26 | ) { 27 | this.element = element; 28 | this.options = new Options(options); 29 | this.guild = guild; 30 | 31 | /** 32 | * Create html element for this widget 33 | */ 34 | const contentElement = document.createElement("div"); 35 | 36 | /** 37 | * Mark as discord widget 38 | */ 39 | contentElement.setAttribute("data-discord-widget", "true"); 40 | contentElement.className = "discord-widget"; 41 | 42 | /** 43 | * Create template 44 | */ 45 | contentElement.innerHTML = getWidget(this.guild, this.options); 46 | 47 | this.element.appendChild(contentElement); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-widget", 3 | "version": "1.2.1", 4 | "description": "♻️ Customize discord widget for your website", 5 | "main": "dist/discord-widget.js", 6 | "jsnext:main": "dist/discord-widget.js", 7 | "module": "dist/discord-widget.js", 8 | "types": "dist/index.d.ts", 9 | "author": "Jakub Szołtysek ", 10 | "keywords": [ 11 | "discord", 12 | "widget", 13 | "custom" 14 | ], 15 | "license": "MIT", 16 | "scripts": { 17 | "dev": "cross-env NODE_ENV=development webpack serve", 18 | "build": "cross-env NODE_ENV=production webpack", 19 | "clean": "rm -rf ./dist" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/sveenxx/discord-widget.git" 24 | }, 25 | "devDependencies": { 26 | "@types/lodash": "^4.14.172", 27 | "@typescript-eslint/eslint-plugin": "^4.29.0", 28 | "@typescript-eslint/parser": "^4.29.0", 29 | "@webpack-cli/init": "^1.1.3", 30 | "babel-plugin-syntax-dynamic-import": "^6.18.0", 31 | "circular-dependency-plugin": "^5.2.2", 32 | "clean-webpack-plugin": "^4.0.0-alpha.0", 33 | "cross-env": "^7.0.3", 34 | "eslint": "^7.32.0", 35 | "ts-loader": "^9.2.5", 36 | "typescript": ">=3.0.0", 37 | "uglifyjs-webpack-plugin": "^2.2.0", 38 | "webpack": "^5.49.0", 39 | "webpack-cli": "^4.7.2", 40 | "webpack-dev-server": "^3.11.2", 41 | "webpack-merge": "^5.8.0" 42 | }, 43 | "dependencies": { 44 | "lodash": "^4.17.21" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/templates/widget.ts: -------------------------------------------------------------------------------- 1 | import { template } from "../template"; 2 | import { getMemeberList } from "./widgetMemberList"; 3 | import { WidgetOptions, Guild } from "../interfaces/"; 4 | 5 | export const getWidget = (guild: Guild, options: WidgetOptions): string => 6 | template( 7 | guild, 8 | (data) => 9 | ` 10 |
11 | 12 | ${ 13 | data.presence_count 14 | } Members Online 15 |
16 |
17 |
18 | ${data.channels.map( 19 | (channel) => 20 | `
${channel.name}
` 21 | )} 22 |
23 |
24 |
MEMBERS ONLINE
25 |
26 | ${getMemeberList(guild)} 27 |
28 |
29 |
30 | 38 | ` 39 | ); 40 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { WidgetOptions } from "./interfaces/"; 2 | import { fetchData } from "./utils"; 3 | 4 | import { Widget } from "./widget"; 5 | import { attachStyle, detachStyle } from "./style"; 6 | 7 | declare const WIDGET_VERSION: string; 8 | 9 | export default class DiscordWidget extends Widget { 10 | static version = WIDGET_VERSION; 11 | 12 | /** 13 | * Initializes the widget on the specified element 14 | * 15 | * @param element The DOM element to initialize 16 | * @param guildId Guild identifier 17 | * @param options [optional] Options for the widget 18 | */ 19 | static async init( 20 | element: HTMLElement | null, 21 | guildId: string, 22 | options?: WidgetOptions 23 | ): Promise { 24 | const guild = await fetchData(guildId); 25 | 26 | if (!element || element.nodeType !== 1) { 27 | throw new TypeError( 28 | `exptect element to be DOM Element, but got ${element}` 29 | ); 30 | } 31 | 32 | if (!guild?.id) { 33 | throw new TypeError( 34 | `exptect element to be Server ID, but got ${guildId}` 35 | ); 36 | } 37 | 38 | attachStyle(); 39 | 40 | return new Widget(element, guild, options); 41 | } 42 | 43 | /** 44 | * Attaches default styles to current document. 45 | */ 46 | static attachStyle() { 47 | return attachStyle(); 48 | } 49 | 50 | /** 51 | * Removes default styles from current document. 52 | */ 53 | static detachStyle() { 54 | return detachStyle(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require("webpack"); 3 | const CircularDependencyPlugin = require("circular-dependency-plugin"); 4 | const { CleanWebpackPlugin } = require("clean-webpack-plugin"); 5 | const { merge } = require("webpack-merge"); 6 | 7 | const prodConfig = require("./webpack.prod"); 8 | const devConfig = require("./webpack.dev"); 9 | 10 | const resolveApp = (relativePath) => path.resolve(__dirname, relativePath); 11 | 12 | module.exports = function () { 13 | const isEnvProduction = process.env.NODE_ENV === "production"; 14 | const commonConfig = { 15 | devtool: "source-map", 16 | 17 | entry: "./src/index.ts", 18 | 19 | output: { 20 | filename: "[name].js", 21 | path: path.resolve(__dirname, "dist"), 22 | libraryTarget: "umd", 23 | }, 24 | 25 | resolve: { 26 | extensions: [".ts"], 27 | }, 28 | 29 | module: { 30 | rules: [ 31 | { 32 | test: /\.ts$/, 33 | use: [ 34 | { 35 | loader: "ts-loader", 36 | }, 37 | ], 38 | include: [resolveApp("src")], 39 | exclude: /(node_modules|dist)/, 40 | }, 41 | ], 42 | }, 43 | plugins: [ 44 | new webpack.DefinePlugin({ 45 | WIDGET_VERSION: JSON.stringify( 46 | require(resolveApp("package.json")).version 47 | ), 48 | }), 49 | new CircularDependencyPlugin({ 50 | exclude: /node_modules/, 51 | failOnError: true, 52 | }), 53 | new CleanWebpackPlugin(), 54 | ], 55 | stats: { 56 | modules: false, 57 | }, 58 | }; 59 | 60 | if (isEnvProduction) return merge(commonConfig, prodConfig); 61 | else return merge(commonConfig, devConfig); 62 | }; 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

6 | Easly add custom discord widgets to your website. 7 |

8 | 9 |

10 | 11 | 12 | License: MIT 13 |

14 | 15 | ## Installation 16 | 17 | Via NPM: 18 | 19 | ```bash 20 | $ npm install discord-widget --save 21 | ``` 22 | 23 | Via Yarn (recommended): 24 | 25 | ```bash 26 | $ yarn add discord-widget 27 | ``` 28 | 29 | ## Setup 30 | 31 | ```js 32 | import Widget from "discord-widget"; 33 | 34 | (async function () { 35 | await Widget.init( 36 | document.querySelector("#my-widget-container"), 37 | "server-id" 38 | ); 39 | })(); 40 | ``` 41 | 42 | if you are not using any bundlers 43 | 44 | ```html 45 | 46 | 47 | 55 | ``` 56 | 57 | ## Example 58 | 59 | ```js 60 | import Widget from "discord-widget"; 61 | 62 | const container = document.querySelector("#my-widget-container") 63 | 64 | (async function () { 65 | await Widget.init(container, "600381707073486871"); 66 | 67 | //Use it if you want to disable default styles and use your own 68 | Widget.detachStyle(); 69 | })() 70 | ``` 71 | 72 | ## Contact 73 | 74 | [](https://discord.gg/yWJRMftbaK) 75 | 76 | ## License 77 | 78 | Copyright © 2021 [Jakub Szołtysek](https://github.com/sveenxx).
79 | This project is [MIT](https://github.com/sveenxx/discord-widget/blob/master/LICENSE) licensed. 80 | 81 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '34 14 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /src/style.ts: -------------------------------------------------------------------------------- 1 | const WIDGET_STYLE = ` 2 | @import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;700&display=swap'); 3 | 4 | .discord-widget { 5 | width: 100%; 6 | height: 500px; 7 | max-width: 400px; 8 | background-color: #202225; 9 | border-radius: 5px; 10 | overflow: hidden; 11 | display: -webkit-box; 12 | display: -ms-flexbox; 13 | display: flex; 14 | -webkit-box-orient: vertical; 15 | -webkit-box-direction: normal; 16 | -ms-flex-direction: column; 17 | flex-direction: column; 18 | -webkit-box-flex: 1; 19 | -ms-flex: 1; 20 | flex: 1; 21 | } 22 | 23 | .discord-widget * { 24 | font-family: 'Manrope', sans-serif; 25 | } 26 | 27 | .widget-header { 28 | background-color: #7289da; 29 | height: 100px; 30 | display: -webkit-box; 31 | display: -ms-flexbox; 32 | display: flex; 33 | -webkit-box-pack: justify; 34 | -ms-flex-pack: justify; 35 | justify-content: space-between; 36 | -webkit-box-align: center; 37 | -ms-flex-align: center; 38 | align-items: center; 39 | padding: 0 1em; 40 | } 41 | 42 | .widget-logo { 43 | background-image: url("data:image/svg+xml;base64, PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE3LjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMjQgMzQiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDEyNCAzNCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxnPgoJPGc+CgkJPGVsbGlwc2UgZmlsbD0iI0ZGRkZGRiIgY3g9IjE4IiBjeT0iMTYuMSIgcng9IjEuNyIgcnk9IjEuOSIvPgoJCTxlbGxpcHNlIGZpbGw9IiNGRkZGRkYiIGN4PSIxMS44IiBjeT0iMTYuMSIgcng9IjEuNyIgcnk9IjEuOSIvPgoJCTxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0yNi4zLDBIMy41QzEuNiwwLDAsMS42LDAsMy41djIzQzAsMjguNCwxLjYsMzAsMy41LDMwaDE5LjNsLTAuOS0zLjFsMi4yLDJsMi4xLDEuOWwzLjcsMy4ydi03LjV2LTEuNwoJCQlWMy41QzI5LjgsMS42LDI4LjIsMCwyNi4zLDB6IE0xOS43LDIyLjJjMCwwLTAuNi0wLjctMS4xLTEuNGMyLjItMC42LDMuMS0yLDMuMS0yYy0wLjcsMC41LTEuNCwwLjgtMiwxYy0wLjgsMC40LTEuNywwLjYtMi41LDAuNwoJCQljLTEuNiwwLjMtMy4xLDAuMi00LjQsMGMtMS0wLjItMS44LTAuNS0yLjUtMC43Yy0wLjQtMC4xLTAuOC0wLjMtMS4yLTAuNmMtMC4xLDAtMC4xLTAuMS0wLjItMC4xYzAsMC0wLjEsMC0wLjEsMAoJCQljLTAuMy0wLjItMC41LTAuMy0wLjUtMC4zczAuOCwxLjQsMywyYy0wLjUsMC42LTEuMSwxLjQtMS4xLDEuNEM2LjUsMjIuMSw1LDE5LjYsNSwxOS42YzAtNS41LDIuNC05LjksMi40LTkuOQoJCQljMi40LTEuOCw0LjgtMS44LDQuOC0xLjhsMC4yLDAuMkM5LjQsOSw4LDEwLjQsOCwxMC40czAuNC0wLjIsMS0wLjVjMS44LTAuOCwzLjMtMSwzLjktMS4xYzAuMSwwLDAuMiwwLDAuMywwCgkJCWMxLTAuMSwyLjItMC4yLDMuNCwwYzEuNiwwLjIsMy40LDAuNyw1LjEsMS42YzAsMC0xLjMtMS4zLTQuMi0yLjJsMC4yLTAuM2MwLDAsMi4zLTAuMSw0LjgsMS44YzAsMCwyLjQsNC40LDIuNCw5LjkKCQkJQzI0LjksMTkuNiwyMy41LDIyLjEsMTkuNywyMi4yeiIvPgoJPC9nPgoJPHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTQ1LjYsNy4zaC01Ljd2Ni40bDMuOCwzLjR2LTYuMmgyYzEuMywwLDEuOSwwLjYsMS45LDEuNnY0LjhjMCwxLTAuNiwxLjctMS45LDEuN2gtNS44djMuNmg1LjcKCQljMywwLDUuOS0xLjUsNS45LTV2LTUuMUM1MS40LDguOSw0OC42LDcuMyw0NS42LDcuM3ogTTc1LjMsMTcuNnYtNS4zYzAtMS45LDMuNC0yLjMsNC40LTAuNGwzLjEtMS4zYy0xLjItMi43LTMuNS0zLjUtNS4zLTMuNQoJCWMtMywwLTYsMS44LTYsNS4ydjUuM2MwLDMuNSwzLDUuMiw2LDUuMmMxLjksMCw0LjItMC45LDUuNS0zLjRsLTMuMy0xLjZDNzguNywxOS45LDc1LjMsMTkuNCw3NS4zLDE3LjZ6IE02NSwxMwoJCWMtMS4yLTAuMy0yLTAuNy0yLTEuNGMwLjEtMS44LDIuOC0xLjgsNC40LTAuMWwyLjUtMS45Yy0xLjYtMS45LTMuMy0yLjQtNS4yLTIuNGMtMi44LDAtNS41LDEuNi01LjUsNC42YzAsMi45LDIuMiw0LjUsNC43LDQuOAoJCWMxLjIsMC4yLDIuNiwwLjcsMi42LDEuNWMtMC4xLDEuNi0zLjUsMS42LTUtMC4zTDU5LDIwYzEuNCwxLjgsMy4zLDIuOCw1LjIsMi44YzIuOCwwLDUuOS0xLjYsNi00LjZDNzAuNCwxNC41LDY3LjcsMTMuNSw2NSwxM3oKCQkgTTUzLjUsMjIuNmgzLjhWNy4zaC0zLjhWMjIuNnogTTExOC4xLDcuM2gtNS43djYuNGwzLjgsMy40di02LjJoMmMxLjMsMCwxLjksMC42LDEuOSwxLjZ2NC44YzAsMS0wLjYsMS43LTEuOSwxLjdoLTUuOHYzLjZoNS43CgkJYzMsMCw1LjktMS41LDUuOS01di01LjFDMTI0LDguOSwxMjEuMSw3LjMsMTE4LjEsNy4zeiBNOTAuMiw3LjFjLTMuMiwwLTYuMywxLjctNi4zLDUuMnY1LjJjMCwzLjUsMy4yLDUuMiw2LjMsNS4yCgkJYzMuMiwwLDYuMy0xLjcsNi4zLTUuMnYtNS4yQzk2LjUsOC45LDkzLjQsNy4xLDkwLjIsNy4xeiBNOTIuNywxNy42YzAsMS4xLTEuMiwxLjctMi40LDEuN2MtMS4yLDAtMi41LTAuNS0yLjUtMS43di01LjIKCQljMC0xLjEsMS4yLTEuNywyLjQtMS43YzEuMiwwLDIuNSwwLjUsMi41LDEuN1YxNy42eiBNMTEwLjMsMTIuNGMtMC4xLTMuNi0yLjUtNS01LjYtNWgtNi4xdjE1LjJoMy45di00LjhoMC43bDMuNSw0LjhoNC44CgkJbC00LjEtNS4yQzEwOS4yLDE2LjgsMTEwLjMsMTUuMiwxMTAuMywxMi40eiBNMTA0LjcsMTQuNGgtMi4zdi0zLjVoMi4zQzEwNy4xLDEwLjksMTA3LjEsMTQuNCwxMDQuNywxNC40eiIvPgo8L2c+Cjwvc3ZnPgo="); 44 | width: 124px; 45 | height: 34px; 46 | background-size: cover; 47 | } 48 | 49 | .widget-body { 50 | display: -webkit-box; 51 | display: -ms-flexbox; 52 | display: flex; 53 | -webkit-box-orient: vertical; 54 | -webkit-box-direction: normal; 55 | -ms-flex-direction: column; 56 | flex-direction: column; 57 | padding: 1em 0; 58 | height: calc(100% - 2em); 59 | overflow-y: scroll; 60 | } 61 | 62 | .widget-body::-webkit-scrollbar { 63 | width: 10px; 64 | } 65 | 66 | .widget-body::-webkit-scrollbar { 67 | width: 10px; 68 | } 69 | 70 | .widget-body::-webkit-scrollbar-thumb, 71 | .widget-body::-webkit-scrollbar-track-piece { 72 | background-clip: padding-box; 73 | border: 3px solid transparent; 74 | border-radius: 5px; 75 | } 76 | 77 | .widget-body::-webkit-scrollbar-thumb { 78 | background-color: hsla(0, 0%, 100%, .1); 79 | } 80 | 81 | .widget-footer { 82 | height: 42px; 83 | box-sizing: border-box; 84 | background-color: #202225; 85 | padding: 6px 6px 6px 20px; 86 | -webkit-box-shadow: 0 -1px 18px rgb(0 0 0 / 20%), 0 -1px 0 rgb(0 0 0 / 20%); 87 | box-shadow: 0 -1px 18px rgb(0 0 0 / 20%), 0 -1px 0 rgb(0 0 0 / 20%); 88 | -ms-flex-negative: 0; 89 | flex-shrink: 0; 90 | display: -webkit-box; 91 | display: -ms-flexbox; 92 | display: flex; 93 | -webkit-box-align: center; 94 | -ms-flex-align: center; 95 | align-items: center; 96 | -webkit-box-pack: justify; 97 | -ms-flex-pack: justify; 98 | justify-content: space-between; 99 | } 100 | 101 | .widget-footer-text { 102 | font-weight: 500; 103 | opacity: .1; 104 | color: #fff; 105 | font-size: 13px; 106 | overflow: hidden; 107 | } 108 | 109 | .widget-join-button { 110 | color: #fff!important; 111 | text-decoration: none; 112 | border: 1px solid #212325; 113 | border-radius: 4px; 114 | width: 120px; 115 | height: 100%; 116 | display: -webkit-box; 117 | display: -ms-flexbox; 118 | display: flex; 119 | -webkit-box-align: center; 120 | -ms-flex-align: center; 121 | align-items: center; 122 | -webkit-box-pack: center; 123 | -ms-flex-pack: center; 124 | justify-content: center; 125 | background-color: hsla(0, 0%, 100%, .1); 126 | font-size: 13px; 127 | cursor: pointer; 128 | -webkit-transition: background .1s; 129 | -o-transition: background .1s; 130 | transition: background .1s; 131 | } 132 | 133 | .widget-join-button:hover { 134 | background-color: hsla(0, 0%, 100%, .2); 135 | } 136 | 137 | .widget-wrapper { 138 | padding: 0 1em; 139 | } 140 | 141 | .widget-member { 142 | height: 20px; 143 | display: -webkit-box; 144 | display: -ms-flexbox; 145 | display: flex; 146 | -webkit-box-pack: justify; 147 | -ms-flex-pack: justify; 148 | justify-content: space-between; 149 | margin: 6px 0; 150 | } 151 | 152 | .widget-member>div { 153 | display: -webkit-box; 154 | display: -ms-flexbox; 155 | display: flex; 156 | } 157 | 158 | .widget-member-avatar { 159 | width: 16px; 160 | height: 16px; 161 | position: relative; 162 | margin-right: 4px; 163 | } 164 | 165 | .widget-member-avatar img { 166 | width: 100%; 167 | border-radius: 50%; 168 | } 169 | 170 | .widget-member-status { 171 | width: 6px; 172 | height: 6px; 173 | border-radius: 3px; 174 | position: absolute; 175 | bottom: 0; 176 | right: 0; 177 | background-color: #f04747; 178 | } 179 | 180 | .widget-member-status[data-status="online"] { 181 | background-color: #43b581; 182 | } 183 | 184 | .widget-member-status[data-status="idle"] { 185 | background-color: #faa61a; 186 | } 187 | 188 | .widget-member-name { 189 | font-size: 14px; 190 | color: #8a8e94; 191 | } 192 | 193 | .widget-member-desc { 194 | font-size: 14px; 195 | color: #4f545c; 196 | } 197 | 198 | .widget-title { 199 | margin-bottom: 12px; 200 | color: #8a8e94; 201 | font-size: 14px; 202 | font-weight: 500; 203 | } 204 | 205 | .widget-channel { 206 | font-weight: 600; 207 | font-size: 18px; 208 | margin-bottom: 18px; 209 | color: #fff; 210 | } 211 | 212 | .widget-status { 213 | color: #fff; 214 | font-size: 14px; 215 | } 216 | `; 217 | 218 | const WIDGET_ID = "discord-widget-style"; 219 | 220 | let isAttached = false; 221 | 222 | export function attachStyle(): void { 223 | if (isAttached || typeof window === "undefined") { 224 | return; 225 | } 226 | 227 | const styleElement = document.createElement("style"); 228 | 229 | styleElement.id = WIDGET_ID; 230 | styleElement.textContent = WIDGET_STYLE; 231 | 232 | document.head.appendChild(styleElement); 233 | 234 | isAttached = true; 235 | } 236 | 237 | export function detachStyle(): void { 238 | if (!isAttached || typeof window === "undefined") { 239 | return; 240 | } 241 | 242 | const styleElement = document.getElementById(WIDGET_ID); 243 | 244 | if (!styleElement || !styleElement.parentNode) { 245 | return; 246 | } 247 | 248 | styleElement.parentNode.removeChild(styleElement); 249 | 250 | isAttached = false; 251 | } 252 | -------------------------------------------------------------------------------- /dist/discord-widget.js: -------------------------------------------------------------------------------- 1 | /*! For license information please see discord-widget.js.LICENSE.txt */ 2 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("DiscordWidget",[],t):"object"==typeof exports?exports.DiscordWidget=t():e.DiscordWidget=t()}(this,(function(){return(()=>{"use strict";var e={380:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Options=void 0,t.Options=class{constructor(e={}){this.connectButton=!0,Object.keys(e).forEach((t=>{this[t]=e[t]}))}}},331:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.detachStyle=t.attachStyle=void 0;const n="discord-widget-style";let i=!1;t.attachStyle=function(){if(!i&&"undefined"!=typeof window){const e=document.createElement("style");e.id=n,e.textContent='\n @import url(\'https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;700&display=swap\');\n\n .discord-widget {\n width: 100%;\n height: 500px;\n max-width: 400px;\n background-color: #202225;\n border-radius: 5px;\n overflow: hidden;\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -ms-flex-direction: column;\n flex-direction: column;\n -webkit-box-flex: 1;\n -ms-flex: 1;\n flex: 1;\n }\n\n .discord-widget * {\n font-family: \'Manrope\', sans-serif;\n }\n\n .widget-header {\n background-color: #7289da;\n height: 100px;\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-pack: justify;\n -ms-flex-pack: justify;\n justify-content: space-between;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n padding: 0 1em;\n }\n\n .widget-logo {\n background-image: url("data:image/svg+xml;base64, PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE3LjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMjQgMzQiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDEyNCAzNCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxnPgoJPGc+CgkJPGVsbGlwc2UgZmlsbD0iI0ZGRkZGRiIgY3g9IjE4IiBjeT0iMTYuMSIgcng9IjEuNyIgcnk9IjEuOSIvPgoJCTxlbGxpcHNlIGZpbGw9IiNGRkZGRkYiIGN4PSIxMS44IiBjeT0iMTYuMSIgcng9IjEuNyIgcnk9IjEuOSIvPgoJCTxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0yNi4zLDBIMy41QzEuNiwwLDAsMS42LDAsMy41djIzQzAsMjguNCwxLjYsMzAsMy41LDMwaDE5LjNsLTAuOS0zLjFsMi4yLDJsMi4xLDEuOWwzLjcsMy4ydi03LjV2LTEuNwoJCQlWMy41QzI5LjgsMS42LDI4LjIsMCwyNi4zLDB6IE0xOS43LDIyLjJjMCwwLTAuNi0wLjctMS4xLTEuNGMyLjItMC42LDMuMS0yLDMuMS0yYy0wLjcsMC41LTEuNCwwLjgtMiwxYy0wLjgsMC40LTEuNywwLjYtMi41LDAuNwoJCQljLTEuNiwwLjMtMy4xLDAuMi00LjQsMGMtMS0wLjItMS44LTAuNS0yLjUtMC43Yy0wLjQtMC4xLTAuOC0wLjMtMS4yLTAuNmMtMC4xLDAtMC4xLTAuMS0wLjItMC4xYzAsMC0wLjEsMC0wLjEsMAoJCQljLTAuMy0wLjItMC41LTAuMy0wLjUtMC4zczAuOCwxLjQsMywyYy0wLjUsMC42LTEuMSwxLjQtMS4xLDEuNEM2LjUsMjIuMSw1LDE5LjYsNSwxOS42YzAtNS41LDIuNC05LjksMi40LTkuOQoJCQljMi40LTEuOCw0LjgtMS44LDQuOC0xLjhsMC4yLDAuMkM5LjQsOSw4LDEwLjQsOCwxMC40czAuNC0wLjIsMS0wLjVjMS44LTAuOCwzLjMtMSwzLjktMS4xYzAuMSwwLDAuMiwwLDAuMywwCgkJCWMxLTAuMSwyLjItMC4yLDMuNCwwYzEuNiwwLjIsMy40LDAuNyw1LjEsMS42YzAsMC0xLjMtMS4zLTQuMi0yLjJsMC4yLTAuM2MwLDAsMi4zLTAuMSw0LjgsMS44YzAsMCwyLjQsNC40LDIuNCw5LjkKCQkJQzI0LjksMTkuNiwyMy41LDIyLjEsMTkuNywyMi4yeiIvPgoJPC9nPgoJPHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTQ1LjYsNy4zaC01Ljd2Ni40bDMuOCwzLjR2LTYuMmgyYzEuMywwLDEuOSwwLjYsMS45LDEuNnY0LjhjMCwxLTAuNiwxLjctMS45LDEuN2gtNS44djMuNmg1LjcKCQljMywwLDUuOS0xLjUsNS45LTV2LTUuMUM1MS40LDguOSw0OC42LDcuMyw0NS42LDcuM3ogTTc1LjMsMTcuNnYtNS4zYzAtMS45LDMuNC0yLjMsNC40LTAuNGwzLjEtMS4zYy0xLjItMi43LTMuNS0zLjUtNS4zLTMuNQoJCWMtMywwLTYsMS44LTYsNS4ydjUuM2MwLDMuNSwzLDUuMiw2LDUuMmMxLjksMCw0LjItMC45LDUuNS0zLjRsLTMuMy0xLjZDNzguNywxOS45LDc1LjMsMTkuNCw3NS4zLDE3LjZ6IE02NSwxMwoJCWMtMS4yLTAuMy0yLTAuNy0yLTEuNGMwLjEtMS44LDIuOC0xLjgsNC40LTAuMWwyLjUtMS45Yy0xLjYtMS45LTMuMy0yLjQtNS4yLTIuNGMtMi44LDAtNS41LDEuNi01LjUsNC42YzAsMi45LDIuMiw0LjUsNC43LDQuOAoJCWMxLjIsMC4yLDIuNiwwLjcsMi42LDEuNWMtMC4xLDEuNi0zLjUsMS42LTUtMC4zTDU5LDIwYzEuNCwxLjgsMy4zLDIuOCw1LjIsMi44YzIuOCwwLDUuOS0xLjYsNi00LjZDNzAuNCwxNC41LDY3LjcsMTMuNSw2NSwxM3oKCQkgTTUzLjUsMjIuNmgzLjhWNy4zaC0zLjhWMjIuNnogTTExOC4xLDcuM2gtNS43djYuNGwzLjgsMy40di02LjJoMmMxLjMsMCwxLjksMC42LDEuOSwxLjZ2NC44YzAsMS0wLjYsMS43LTEuOSwxLjdoLTUuOHYzLjZoNS43CgkJYzMsMCw1LjktMS41LDUuOS01di01LjFDMTI0LDguOSwxMjEuMSw3LjMsMTE4LjEsNy4zeiBNOTAuMiw3LjFjLTMuMiwwLTYuMywxLjctNi4zLDUuMnY1LjJjMCwzLjUsMy4yLDUuMiw2LjMsNS4yCgkJYzMuMiwwLDYuMy0xLjcsNi4zLTUuMnYtNS4yQzk2LjUsOC45LDkzLjQsNy4xLDkwLjIsNy4xeiBNOTIuNywxNy42YzAsMS4xLTEuMiwxLjctMi40LDEuN2MtMS4yLDAtMi41LTAuNS0yLjUtMS43di01LjIKCQljMC0xLjEsMS4yLTEuNywyLjQtMS43YzEuMiwwLDIuNSwwLjUsMi41LDEuN1YxNy42eiBNMTEwLjMsMTIuNGMtMC4xLTMuNi0yLjUtNS01LjYtNWgtNi4xdjE1LjJoMy45di00LjhoMC43bDMuNSw0LjhoNC44CgkJbC00LjEtNS4yQzEwOS4yLDE2LjgsMTEwLjMsMTUuMiwxMTAuMywxMi40eiBNMTA0LjcsMTQuNGgtMi4zdi0zLjVoMi4zQzEwNy4xLDEwLjksMTA3LjEsMTQuNCwxMDQuNywxNC40eiIvPgo8L2c+Cjwvc3ZnPgo=");\n width: 124px;\n height: 34px;\n background-size: cover;\n }\n\n .widget-body {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -ms-flex-direction: column;\n flex-direction: column;\n padding: 1em 0;\n height: calc(100% - 2em);\n overflow-y: scroll;\n }\n\n .widget-body::-webkit-scrollbar {\n width: 10px;\n }\n\n .widget-body::-webkit-scrollbar {\n width: 10px;\n }\n\n .widget-body::-webkit-scrollbar-thumb,\n .widget-body::-webkit-scrollbar-track-piece {\n background-clip: padding-box;\n border: 3px solid transparent;\n border-radius: 5px;\n }\n\n .widget-body::-webkit-scrollbar-thumb {\n background-color: hsla(0, 0%, 100%, .1);\n }\n\n .widget-footer {\n height: 42px;\n box-sizing: border-box;\n background-color: #202225;\n padding: 6px 6px 6px 20px;\n -webkit-box-shadow: 0 -1px 18px rgb(0 0 0 / 20%), 0 -1px 0 rgb(0 0 0 / 20%);\n box-shadow: 0 -1px 18px rgb(0 0 0 / 20%), 0 -1px 0 rgb(0 0 0 / 20%);\n -ms-flex-negative: 0;\n flex-shrink: 0;\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n -webkit-box-pack: justify;\n -ms-flex-pack: justify;\n justify-content: space-between;\n }\n\n .widget-footer-text {\n font-weight: 500;\n opacity: .1;\n color: #fff;\n font-size: 13px;\n overflow: hidden;\n }\n\n .widget-join-button {\n color: #fff!important;\n text-decoration: none;\n border: 1px solid #212325;\n border-radius: 4px;\n width: 120px;\n height: 100%;\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n -webkit-box-pack: center;\n -ms-flex-pack: center;\n justify-content: center;\n background-color: hsla(0, 0%, 100%, .1);\n font-size: 13px;\n cursor: pointer;\n -webkit-transition: background .1s;\n -o-transition: background .1s;\n transition: background .1s;\n }\n\n .widget-join-button:hover {\n background-color: hsla(0, 0%, 100%, .2);\n }\n\n .widget-wrapper {\n padding: 0 1em;\n }\n\n .widget-member {\n height: 20px;\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-pack: justify;\n -ms-flex-pack: justify;\n justify-content: space-between;\n margin: 6px 0;\n }\n\n .widget-member>div {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n }\n\n .widget-member-avatar {\n width: 16px;\n height: 16px;\n position: relative;\n margin-right: 4px;\n }\n\n .widget-member-avatar img {\n width: 100%;\n border-radius: 50%;\n }\n\n .widget-member-status {\n width: 6px;\n height: 6px;\n border-radius: 3px;\n position: absolute;\n bottom: 0;\n right: 0;\n background-color: #f04747;\n }\n\n .widget-member-status[data-status="online"] {\n background-color: #43b581;\n }\n\n .widget-member-status[data-status="idle"] {\n background-color: #faa61a;\n }\n\n .widget-member-name {\n font-size: 14px;\n color: #8a8e94;\n }\n\n .widget-member-desc {\n font-size: 14px;\n color: #4f545c;\n }\n\n .widget-title {\n margin-bottom: 12px;\n color: #8a8e94;\n font-size: 14px;\n font-weight: 500;\n }\n\n .widget-channel {\n font-weight: 600;\n font-size: 18px;\n margin-bottom: 18px;\n color: #fff;\n }\n\n .widget-status {\n color: #fff;\n font-size: 14px;\n }\n',document.head.appendChild(e),i=!0}},t.detachStyle=function(){if(i&&"undefined"!=typeof window){const e=document.getElementById(n);e&&e.parentNode&&(e.parentNode.removeChild(e),i=!1)}}},704:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.template=void 0,t.template=function(e,t){return t(e)}},800:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),n(655).__exportStar(n(215),t)},215:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.getWidget=void 0;const i=n(704),o=n(333);t.getWidget=(e,t)=>i.template(e,(n=>`\n
\n \n ${n.presence_count} Members Online\n
\n
\n
\n ${n.channels.map((e=>`
${e.name}
`))}\n
\n
\n
MEMBERS ONLINE
\n
\n ${o.getMemeberList(e)}\n
\n
\n
\n \n `))},333:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.getMemeberList=void 0;const i=n(704);t.getMemeberList=e=>i.template(e,(e=>e.members.map((e=>`\n
\n
\n
\n \n \n
\n ${e.username}\n
\n ${void 0===e.game?"":`${e.game.name}`}\n
\n`)).join("")))},363:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.fetchData=void 0,t.fetchData=async function(e){try{return await(await fetch(`https://discord.com/api/guilds/${e}/widget.json`)).json()}catch(e){throw new Error(e)}}},928:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0});const i=n(655);i.__exportStar(n(363),t),i.__exportStar(n(807),t)},807:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.setStyle=void 0,t.setStyle=function(e,t){Object.keys(t).forEach((n=>{e.style[n.split(/(?=[A-Z])/).join("-").toLowerCase()]=t[n]}))}},891:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Widget=void 0;const i=n(800),o=n(380);t.Widget=class{constructor(e,t,n){this.element=e,this.options=new o.Options(n),this.guild=t;const r=document.createElement("div");r.setAttribute("data-discord-widget","true"),r.className="discord-widget",r.innerHTML=i.getWidget(this.guild,this.options),this.element.appendChild(r)}}},655:(e,t,n)=>{n.r(t),n.d(t,{__extends:()=>function(e,t){function n(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)},__assign:()=>o,__rest:()=>function(e,t){var n={};for(o in e)Object.prototype.hasOwnProperty.call(e,o)&&t.indexOf(o)<0&&(n[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,o=Object.getOwnPropertySymbols(e);ifunction(e,t,n,i){var o,r=arguments.length,a=r<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,n,i);else for(var s=e.length-1;0<=s;s--)(o=e[s])&&(a=(r<3?o(a):3function(e,t){return function(n,i){t(n,i,e)}},__metadata:()=>function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},__awaiter:()=>function(e,t,n,i){return new(n=n||Promise)((function(o,r){function a(e){try{u(i.next(e))}catch(e){r(e)}}function s(e){try{u(i.throw(e))}catch(e){r(e)}}function u(e){var t;e.done?o(e.value):((t=e.value)instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((i=i.apply(e,t||[])).next())}))},__generator:()=>function(e,t){var n,i,o,r,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return r={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(r[Symbol.iterator]=function(){return this}),r;function s(r){return function(s){return function(r){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,i&&(o=2&r[0]?i.return:r[0]?i.throw||((o=i.return)&&o.call(i),0):i.next)&&!(o=o.call(i,r[1])).done)return o;switch(i=0,(r=o?[2&r[0],o.value]:r)[0]){case 0:case 1:o=r;break;case 4:return a.label++,{value:r[1],done:!1};case 5:a.label++,i=r[1],r=[0];continue;case 7:r=a.ops.pop(),a.trys.pop();continue;default:if(!(o=0<(o=a.trys).length&&o[o.length-1])&&(6===r[0]||2===r[0])){a=0;continue}if(3===r[0]&&(!o||r[1]>o[0]&&r[1]function(e,t,n,i){void 0===i&&(i=n),e[i]=t[n]},__exportStar:()=>function(e,t){for(var n in e)"default"===n||t.hasOwnProperty(n)||(t[n]=e[n])},__values:()=>r,__read:()=>a,__spread:()=>function(){for(var e=[],t=0;tfunction(){for(var e=0,t=0,n=arguments.length;ts,__asyncGenerator:()=>function(e,t,n){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var i,o=n.apply(e,t||[]),r=[];return i={},a("next"),a("throw"),a("return"),i[Symbol.asyncIterator]=function(){return this},i;function a(e){o[e]&&(i[e]=function(t){return new Promise((function(n,i){1function(e){var t,n;return t={},i("next"),i("throw",(function(e){throw e})),i("return"),t[Symbol.iterator]=function(){return this},t;function i(i,o){t[i]=e[i]?function(t){return(n=!n)?{value:s(e[i](t)),done:"return"===i}:o?o(t):t}:o}},__asyncValues:()=>function(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,n=e[Symbol.asyncIterator];return n?n.call(e):(e=r(e),t={},i("next"),i("throw"),i("return"),t[Symbol.asyncIterator]=function(){return this},t);function i(n){t[n]=e[n]&&function(t){return new Promise((function(i,o){var r,a;t=e[n](t),r=i,i=o,a=t.done,o=t.value,Promise.resolve(o).then((function(e){r({value:e,done:a})}),i)}))}}},__makeTemplateObject:()=>function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e},__importStar:()=>function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t},__importDefault:()=>function(e){return e&&e.__esModule?e:{default:e}},__classPrivateFieldGet:()=>function(e,t){if(t.has(e))return t.get(e);throw new TypeError("attempted to get private field on non-instance")},__classPrivateFieldSet:()=>function(e,t,n){if(t.has(e))return t.set(e,n),n;throw new TypeError("attempted to set private field on non-instance")}});var i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)},o=function(){return(o=Object.assign||function(e){for(var t,n=1,i=arguments.length;n=e.length?void 0:e)&&e[i++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function a(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var i,o,r=n.call(e),a=[];try{for(;(void 0===t||0{for(var i in t)n.o(t,i)&&!n.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var i={};return(()=>{var e=i;const t=n(928),o=n(891),r=n(331);class a extends o.Widget{static async init(e,n,i){var a=await t.fetchData(n);if(!e||1!==e.nodeType)throw new TypeError(`exptect element to be DOM Element, but got ${e}`);if(null==a||!a.id)throw new TypeError(`exptect element to be Server ID, but got ${n}`);return r.attachStyle(),new o.Widget(e,a,i)}static attachStyle(){return r.attachStyle()}static detachStyle(){return r.detachStyle()}}(e.default=a).version="1.2.0"})(),i.default})()})); --------------------------------------------------------------------------------