working on discord
This commit is contained in:
parent
e9e5e8f7ec
commit
8504775ff3
17 changed files with 195 additions and 54 deletions
|
@ -14,6 +14,7 @@ Very unfinished!! Mostly a programming exercise for me for now to practice fulls
|
|||
- [ ] Sanitize user inputs
|
||||
- [ ] Check status of matterbridge processes
|
||||
- [ ] Complete slack login workflow
|
||||
- [ ] Kill group processes & delete config when group is deleted
|
||||
|
||||
## Supported Clients
|
||||
|
||||
|
|
7
client/src/api/discord.ts
Normal file
7
client/src/api/discord.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
export const getDiscordInstallURL = (callback: CallableFunction) => {
|
||||
fetch('api/discord/install')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
callback(res.data.url)
|
||||
})
|
||||
}
|
|
@ -11,3 +11,17 @@ export const groupInvite = (token: string, callback: CallableFunction) => {
|
|||
.then(result => result.json())
|
||||
.then(result => callback(result))
|
||||
}
|
||||
|
||||
export const deleteGroup = (id: string, callback: CallableFunction) => {
|
||||
fetch('api/groups', {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: id
|
||||
})
|
||||
})
|
||||
.then(result => result.json())
|
||||
.then(result => callback(result))
|
||||
}
|
|
@ -9,10 +9,12 @@ import TableContainer from '@mui/material/TableContainer';
|
|||
import TableHead from '@mui/material/TableHead';
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
|
||||
import GroupRow from "./groupRow";
|
||||
|
||||
export default function GroupPanel({
|
||||
groups
|
||||
groups,
|
||||
fetchGroups
|
||||
}){
|
||||
|
||||
return(
|
||||
|
@ -24,6 +26,7 @@ export default function GroupPanel({
|
|||
<TableCell>Group</TableCell>
|
||||
<TableCell align="right">Created At</TableCell>
|
||||
<TableCell align="right">Invite Token</TableCell>
|
||||
<TableCell align="right">Delete</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
|
@ -34,6 +37,7 @@ export default function GroupPanel({
|
|||
id={group.id}
|
||||
invite_token={group.invite_token}
|
||||
created_at={group.created_at}
|
||||
fetchGroups={fetchGroups}
|
||||
></GroupRow>
|
||||
)) : undefined}
|
||||
</TableBody>
|
||||
|
|
|
@ -2,17 +2,30 @@ import TableCell from '@mui/material/TableCell';
|
|||
import TableContainer from '@mui/material/TableContainer';
|
||||
import TableHead from '@mui/material/TableHead';
|
||||
import TableRow from '@mui/material/TableRow';
|
||||
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
|
||||
import IconButton from "@mui/material/IconButton"
|
||||
|
||||
import {deleteGroup} from "../../api/groups";
|
||||
|
||||
export interface GroupRowProps {
|
||||
name: string;
|
||||
id: string;
|
||||
invite_token: string;
|
||||
created_at: string;
|
||||
fetchGroups: CallableFunction;
|
||||
}
|
||||
|
||||
export default function GroupRow(
|
||||
{name, id, invite_token, created_at}: GroupRowProps
|
||||
{name, id, invite_token, created_at, fetchGroups}: GroupRowProps
|
||||
){
|
||||
const handleDeleteGroup = () => {
|
||||
deleteGroup(id, deleteGroupCallback)
|
||||
}
|
||||
|
||||
const deleteGroupCallback = () => {
|
||||
fetchGroups()
|
||||
}
|
||||
|
||||
return(
|
||||
<TableRow
|
||||
key={id}
|
||||
|
@ -23,6 +36,13 @@ export default function GroupRow(
|
|||
</TableCell>
|
||||
<TableCell align="right">{created_at}</TableCell>
|
||||
<TableCell align="right">{invite_token}</TableCell>
|
||||
<TableCell align="right">
|
||||
<IconButton
|
||||
onClick={handleDeleteGroup}
|
||||
>
|
||||
<DeleteForeverIcon/>
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
|
||||
|
|
|
@ -9,9 +9,11 @@ import FormControl from '@mui/material/FormControl';
|
|||
import Select from '@mui/material/Select';
|
||||
|
||||
import {SlackLogin} from "../platforms/slackLogin";
|
||||
import {DiscordLogin} from "../platforms/discordLogin";
|
||||
|
||||
enum PLATFORMS {
|
||||
Slack = 'Slack'
|
||||
Slack = 'Slack',
|
||||
Discord = 'Discord'
|
||||
}
|
||||
|
||||
export const JoinLogin = ({
|
||||
|
@ -22,7 +24,6 @@ export const JoinLogin = ({
|
|||
stepComplete,
|
||||
setStepComplete
|
||||
}) => {
|
||||
const [loginComponent, setLoginComponent] = useState(<></>);
|
||||
|
||||
|
||||
const handleSelect = (event) => {
|
||||
|
@ -30,19 +31,6 @@ export const JoinLogin = ({
|
|||
setPlatform(event.target.value);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
switch(platform){
|
||||
case "Slack":
|
||||
setLoginComponent(<SlackLogin
|
||||
bridge={bridge}
|
||||
setBridge={setBridge}
|
||||
/>)
|
||||
default:
|
||||
setLoginComponent(<></>)
|
||||
|
||||
}
|
||||
}, [platform])
|
||||
|
||||
useEffect(() => {
|
||||
if (bridge !== undefined) {
|
||||
setStepComplete({...stepComplete, login:true})
|
||||
|
@ -60,11 +48,20 @@ export const JoinLogin = ({
|
|||
label={"Select Platform"}
|
||||
>
|
||||
<MenuItem value={'Slack'}>Slack</MenuItem>
|
||||
<MenuItem value={'Discord'}>Discord</MenuItem>
|
||||
|
||||
</Select>
|
||||
</FormControl>
|
||||
{
|
||||
platform ? loginComponent : <div style={{width: "50%"}}></div>
|
||||
platform === "Slack" ? <SlackLogin
|
||||
bridge={bridge}
|
||||
setBridge={setBridge}
|
||||
/>
|
||||
: platform === "Discord" ? <DiscordLogin
|
||||
bridge={bridge}
|
||||
setBridge={setBridge}
|
||||
/>
|
||||
: <div style={{width: "50%"}}></div>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
|
|
@ -135,6 +135,7 @@ export default function ManagePanel(){
|
|||
loggedIn ?
|
||||
<GroupPanel
|
||||
groups={groups}
|
||||
fetchGroups={fetchGroups}
|
||||
/>
|
||||
:
|
||||
null
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import {useEffect, useRef, useState} from "react";
|
||||
import {getDiscordInstallURL} from "../../api/discord";
|
||||
import {getBridgeByStateToken} from "../../api/bridge";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
|
||||
export const DiscordLogin = ({
|
||||
bridge,
|
||||
setBridge
|
||||
}) => {
|
||||
const [installLink, setInstallLink] = useState();
|
||||
const [installStarted, setInstallStarted] = useState(false)
|
||||
const pingTimeout = useRef(null);
|
||||
|
||||
const openInstallTab = () => {
|
||||
window.open(installLink, '_blank').focus();
|
||||
setInstallStarted(true)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getDiscordInstallURL(handleInstallLink)
|
||||
}, [])
|
||||
|
||||
const handleInstallLink = (url) => {
|
||||
setInstallLink(url);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const pingForBridge = () => {
|
||||
if (bridge === undefined) {
|
||||
getBridgeByStateToken(setBridge);
|
||||
pingTimeout.current = setTimeout(pingForBridge, 1000);
|
||||
}
|
||||
}
|
||||
if (installStarted) {
|
||||
if (bridge === undefined){
|
||||
pingForBridge()
|
||||
}
|
||||
|
||||
}
|
||||
return () => {clearInterval(pingTimeout.current)}
|
||||
|
||||
|
||||
}, [installStarted, bridge])
|
||||
|
||||
return(
|
||||
<Button
|
||||
variant={"outlined"}
|
||||
onClick={openInstallTab}
|
||||
color={bridge !== undefined ? 'success': undefined}
|
||||
disabled={installLink === undefined}
|
||||
>
|
||||
{installLink === undefined ? 'Waiting for Install Link...' : 'Add to Slack'}
|
||||
</Button>
|
||||
)
|
||||
}
|
|
@ -6,7 +6,7 @@ import {getBridgeByStateToken} from "../../api/bridge";
|
|||
export const SlackLogin = ({
|
||||
bridge,
|
||||
setBridge
|
||||
}) => {
|
||||
}) => {
|
||||
const [installLink, setInstallLink] = useState();
|
||||
const [installStarted, setInstallStarted] = useState(false)
|
||||
const pingTimeout = useRef(null);
|
||||
|
|
|
@ -21,7 +21,6 @@ export const checkAdminToken = async(
|
|||
});
|
||||
} else {
|
||||
req.session.logged_in = false;
|
||||
|
||||
logger.warning("Login with admin token failed")
|
||||
return res.status(403).json({
|
||||
status:'fail',
|
||||
|
|
|
@ -20,6 +20,13 @@ export const DiscordInstallLinkHandler = async(
|
|||
|
||||
const url = `https://discordapp.com/oauth2/authorize?&client_id=${discordConfig.client_id}&scope=bot&permissions=536870912&state=${state_token}`
|
||||
|
||||
res.status(200).json({
|
||||
status: 'success',
|
||||
data: {
|
||||
url
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,26 @@ export const getGroupWithInviteHandler = async(
|
|||
|
||||
}
|
||||
|
||||
export const deleteGroupHandler = async(
|
||||
req: Request,
|
||||
res:Response,
|
||||
) => {
|
||||
let group = await groupRepository.findOneBy({id: req.body.id})
|
||||
if (group){
|
||||
await groupRepository.remove(group)
|
||||
return res.status(200).json({
|
||||
status: 'success',
|
||||
message: 'Group successfully deleted!'
|
||||
})
|
||||
} else {
|
||||
return res.status(404).json({
|
||||
status: 'failed',
|
||||
message: 'no group with matching ID found'
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const getGroupsHandler = async(
|
||||
req: Request,
|
||||
res: Response
|
||||
|
|
|
@ -1,20 +1,28 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
import config from "config";
|
||||
import { Logger, format, createLogger, transports } from 'winston';
|
||||
// import { Logger, format, createLogger, transports } from 'winston';
|
||||
const winston = require('winston');
|
||||
let Logger = winston.Logger
|
||||
let format = winston.format
|
||||
let createLogger = winston.createLogger
|
||||
let transports = winston.transports
|
||||
|
||||
const logDir = config.get<string>('logDir')
|
||||
|
||||
const makeLogdir = () => {
|
||||
if (!fs.existsSync(logDir)){
|
||||
fs.mkdir(logDir, (err:Error|undefined) => {
|
||||
if (err) throw err;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const makeLogger = (): Logger => {
|
||||
const makeLogger = (): typeof Logger => {
|
||||
makeLogdir()
|
||||
|
||||
const logger = createLogger({
|
||||
levels: winston.config.syslog.levels,
|
||||
level: process.env.NODE_ENV === 'development' ? 'debug' : 'info',
|
||||
format: format.combine(
|
||||
format.timestamp({
|
||||
|
@ -26,11 +34,11 @@ const makeLogger = (): Logger => {
|
|||
),
|
||||
transports: [
|
||||
new transports.File({
|
||||
filename: path.join([logDir, 'chatbridge.error.log']),
|
||||
filename: path.join(logDir, 'chatbridge.error.log'),
|
||||
level: 'error'
|
||||
}),
|
||||
new transports.File({
|
||||
filename: path.join([logDir, 'chatbridge.debug.log']),
|
||||
filename: path.join(logDir, 'chatbridge.debug.log'),
|
||||
level: 'debug'
|
||||
}),
|
||||
...(process.env.NODE_ENV === 'development' ? [
|
||||
|
|
|
@ -175,7 +175,7 @@ export const writeTOML = (gateway_toml: object, out_file: string) => {
|
|||
}
|
||||
)
|
||||
|
||||
logger.debug('toml string', toml_string)
|
||||
// logger.debug('toml string', toml_string)
|
||||
|
||||
fs.writeFileSync(out_file, toml_string)
|
||||
|
||||
|
|
|
@ -58,7 +58,9 @@ class MatterbridgeManager {
|
|||
let group_name_slug = slugify(group_name)
|
||||
let group_filename = `${this.matterbridge_config_dir}/matterbridge-${group_name_slug}.toml`
|
||||
await writeGroupConfig(group_name, group_filename);
|
||||
if (!this.process_list.includes(group_name_slug)){
|
||||
|
||||
pm2.connect(async(err:any) => {
|
||||
if (!this.process_list.includes(group_name_slug)) {
|
||||
logger.info('Spawning new matterbridge process: %s', group_name_slug)
|
||||
await pm2.start(
|
||||
{
|
||||
|
@ -67,7 +69,7 @@ class MatterbridgeManager {
|
|||
args: `-conf ${group_filename}`,
|
||||
interpreter: 'none'
|
||||
},
|
||||
(err:any, apps:object) => {
|
||||
(err: any, apps: object) => {
|
||||
if (err) {
|
||||
logger.error('error starting matterbridge process', err, apps)
|
||||
}
|
||||
|
@ -76,12 +78,15 @@ class MatterbridgeManager {
|
|||
this.process_list.push(group_name_slug)
|
||||
} else {
|
||||
logger.info('Restarting existing matterbridge process: %s', group_name_slug)
|
||||
await pm2.restart(group_name_slug, (err:any, proc:any) => {
|
||||
await pm2.restart(group_name_slug, (err: any, proc: any) => {
|
||||
if (err) {
|
||||
logger.error('error restarting matterbridge process', err)
|
||||
}}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
// await pm2.disconnect()
|
||||
})
|
||||
}
|
||||
|
||||
async refreshConfig(group_name: string) {
|
||||
|
@ -111,7 +116,7 @@ class MatterbridgeManager {
|
|||
status: proc.pm2_env.status,
|
||||
monit: proc.monit,
|
||||
pm2_env: {
|
||||
created_at: proc.pm2_env.proc.pm2_env.created_at,
|
||||
created_at: proc.pm2_env.created_at,
|
||||
exec_interpreter: proc.pm2_env.exec_interpreter,
|
||||
exec_mode: proc.pm2_env.exec_mode,
|
||||
instances: proc.pm2_env.instances,
|
||||
|
|
|
@ -3,7 +3,8 @@ import express from 'express';
|
|||
import {
|
||||
createGroupHandler,
|
||||
getGroupHandler,
|
||||
getGroupWithInviteHandler
|
||||
getGroupWithInviteHandler,
|
||||
deleteGroupHandler
|
||||
} from "../controllers/group.controller";
|
||||
|
||||
import {
|
||||
|
@ -19,6 +20,7 @@ router
|
|||
.route('/')
|
||||
.post(validate(createGroupSchema), requireAdmin, createGroupHandler)
|
||||
.get(requireAdmin, getGroupHandler)
|
||||
.delete(requireAdmin, deleteGroupHandler)
|
||||
|
||||
router
|
||||
.route('/invite')
|
||||
|
|
Loading…
Reference in a new issue