mirror of
https://github.com/mgerb/go-discord-bot
synced 2026-01-11 01:22:48 +00:00
statistics tab for discord chat
This commit is contained in:
@@ -9,6 +9,7 @@ import { NotFound } from './pages/NotFound/NotFound';
|
||||
import { Downloader } from './pages/Downloader/Downloader';
|
||||
import { Clips } from './pages/Clips';
|
||||
import { Oauth } from './pages/oauth/oauth';
|
||||
import { Stats } from './pages/stats/stats';
|
||||
import 'babel-polyfill';
|
||||
|
||||
const App: any = (): any => {
|
||||
@@ -21,6 +22,7 @@ const App: any = (): any => {
|
||||
<Route path="/downloader" component={Downloader} />
|
||||
<Route path="/clips" component={Clips} />
|
||||
<Route path="/oauth" component={Oauth} />
|
||||
<Route path="/stats" component={Stats} />
|
||||
<Route component={NotFound} />
|
||||
</Switch>
|
||||
</Wrapper>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import jwt_decode from 'jwt-decode';
|
||||
|
||||
import { StorageService } from '../../services';
|
||||
import './Navbar.scss';
|
||||
import { storage } from '../../storage';
|
||||
|
||||
let oauthUrl: string;
|
||||
|
||||
@@ -31,25 +30,24 @@ export class Navbar extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const token = storage.getJWT();
|
||||
const token = StorageService.getJWT();
|
||||
|
||||
if (token) {
|
||||
const claims: any = jwt_decode(token!);
|
||||
console.log(claims);
|
||||
const email = claims['email'];
|
||||
this.setState({ token, email });
|
||||
}
|
||||
}
|
||||
|
||||
private logout = () => {
|
||||
localStorage.clear();
|
||||
StorageService.clear();
|
||||
window.location.href = '/';
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="Navbar">
|
||||
<div className="Navbar__header">Go Discord Bot</div>
|
||||
<div className="Navbar__header">Cash</div>
|
||||
<NavLink exact to="/" className="Navbar__item" activeClassName="Navbar__item--active">
|
||||
Home
|
||||
</NavLink>
|
||||
@@ -62,6 +60,9 @@ export class Navbar extends React.Component<Props, State> {
|
||||
<NavLink to="/clips" className="Navbar__item" activeClassName="Navbar__item--active">
|
||||
Clips
|
||||
</NavLink>
|
||||
<NavLink to="/stats" className="Navbar__item" activeClassName="Navbar__item--active">
|
||||
Stats
|
||||
</NavLink>
|
||||
|
||||
{!this.state.token ? (
|
||||
<a href={oauthUrl} className="Navbar__item">
|
||||
|
||||
@@ -51,8 +51,8 @@ export class SoundList extends React.Component<Props, State> {
|
||||
const { soundList, type } = this.props;
|
||||
|
||||
return (
|
||||
<div className="Card">
|
||||
<div className="Card__header" style={{ display: 'flex' }}>
|
||||
<div className="card">
|
||||
<div className="card__header" style={{ display: 'flex' }}>
|
||||
<div>
|
||||
<span>{type}</span>
|
||||
<i className="fa fa fa-volume-up" aria-hidden="true" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import axios from 'axios';
|
||||
import { axios } from '../../services';
|
||||
|
||||
import { SoundList, SoundType } from '../../components/SoundList';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import axios from 'axios';
|
||||
import { axios } from '../../services';
|
||||
|
||||
import './Downloader.scss';
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.Home {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ interface State {}
|
||||
export class Home extends React.Component<Props, State> {
|
||||
render() {
|
||||
return (
|
||||
<div className="Home">
|
||||
<div className="Card">
|
||||
<div className="Card__header">Go Discord Bot</div>
|
||||
<div className="content">
|
||||
<div className="card">
|
||||
<div className="card__header">Go Discord Bot</div>
|
||||
|
||||
<h3>04-09-18 Update</h3>
|
||||
<ul>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import axios from 'axios';
|
||||
import { axios } from '../../services';
|
||||
import * as _ from 'lodash';
|
||||
import './Pubg.scss';
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import React from 'react';
|
||||
import Dropzone from 'react-dropzone';
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
|
||||
import { axios } from '../../services';
|
||||
import { SoundList, SoundType } from '../../components/SoundList';
|
||||
|
||||
import './Soundboard.scss';
|
||||
import { storage } from '../../storage';
|
||||
import { AxiosRequestConfig } from 'axios';
|
||||
|
||||
let self: any;
|
||||
|
||||
@@ -37,7 +35,6 @@ export class Soundboard extends React.Component<Props, State> {
|
||||
this.config = {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
Authorization: `Bearer ${storage.getJWT()}`,
|
||||
},
|
||||
onUploadProgress: progressEvent => {
|
||||
this.setState({
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import axios from 'axios';
|
||||
import { axios, StorageService } from '../../services';
|
||||
import queryString from 'query-string';
|
||||
import { RouteComponentProps } from 'react-router-dom';
|
||||
import { storage } from '../../storage';
|
||||
|
||||
interface Props extends RouteComponentProps<any> {}
|
||||
|
||||
@@ -25,7 +24,7 @@ export class Oauth extends React.Component<Props, State> {
|
||||
private async fetchOauth(code: string) {
|
||||
try {
|
||||
const res = await axios.post('/api/oauth', { code });
|
||||
storage.setJWT(res.data);
|
||||
StorageService.setJWT(res.data);
|
||||
window.location.href = '/';
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
3
client/app/pages/stats/stats.scss
Normal file
3
client/app/pages/stats/stats.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.Stats {
|
||||
padding: 10px;
|
||||
}
|
||||
70
client/app/pages/stats/stats.tsx
Normal file
70
client/app/pages/stats/stats.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import React, { Component } from 'react';
|
||||
import { HorizontalBar } from 'react-chartjs-2';
|
||||
import { chain, map } from 'lodash';
|
||||
import { axios } from '../../services';
|
||||
import './stats.scss';
|
||||
|
||||
interface IState {
|
||||
data: {
|
||||
username: string;
|
||||
count: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
/**
|
||||
* a page to show discord chat statistics
|
||||
* currently keeps track of number messages that contain external links
|
||||
*/
|
||||
export class Stats extends Component<any, IState> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getdata();
|
||||
}
|
||||
|
||||
async getdata() {
|
||||
const messages = await axios.get('/api/logger/linkedmessages');
|
||||
const data: any = chain(messages.data)
|
||||
.map((v, k) => {
|
||||
return { username: k, count: v };
|
||||
})
|
||||
.orderBy(v => v.count, 'desc')
|
||||
.value();
|
||||
|
||||
this.setState({ data });
|
||||
}
|
||||
|
||||
render() {
|
||||
const data: any = {
|
||||
labels: map(this.state.data, v => v.username),
|
||||
datasets: [
|
||||
{
|
||||
label: 'Count',
|
||||
backgroundColor: 'rgba(114,137,218, 0.4)',
|
||||
borderColor: 'rgba(114,137,218, 0.9)',
|
||||
borderWidth: 1,
|
||||
hoverBackgroundColor: 'rgba(114,137,218, 0.6)',
|
||||
hoverBorderColor: 'rgba(114,137,218, 1)',
|
||||
data: map(this.state.data, v => v.count),
|
||||
},
|
||||
],
|
||||
options: {
|
||||
responsive: true,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="content">
|
||||
<div className="card" style={{ maxWidth: '1000px' }}>
|
||||
<div className="card__header">Shitposts</div>
|
||||
<HorizontalBar data={data} height={500} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,10 @@ body {
|
||||
padding-left: $navbarWidth;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.input {
|
||||
border-radius: 3px;
|
||||
border: 1px solid $lightGray;
|
||||
@@ -56,7 +60,7 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.Card {
|
||||
.card {
|
||||
background-color: $gray2;
|
||||
border-radius: 5px;
|
||||
max-width: 800px;
|
||||
@@ -65,7 +69,7 @@ body {
|
||||
border: 1px solid $gray3;
|
||||
}
|
||||
|
||||
.Card__header {
|
||||
.card__header {
|
||||
margin: -10px -10px 10px -10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
19
client/app/services/axios.service.ts
Normal file
19
client/app/services/axios.service.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import ax from 'axios';
|
||||
import { StorageService } from './storage.service';
|
||||
|
||||
export const axios = ax.create();
|
||||
|
||||
axios.interceptors.request.use(
|
||||
config => {
|
||||
const jwt = StorageService.getJWT();
|
||||
if (jwt) {
|
||||
config.headers['Authorization'] = `Bearer ${jwt}`;
|
||||
}
|
||||
// Do something before request is sent
|
||||
return config;
|
||||
},
|
||||
function(error) {
|
||||
// Do something with request error
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
2
client/app/services/index.ts
Normal file
2
client/app/services/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './axios.service';
|
||||
export * from './storage.service';
|
||||
@@ -1,3 +1,7 @@
|
||||
const clear = () => {
|
||||
localStorage.clear();
|
||||
};
|
||||
|
||||
const setJWT = (token: string) => {
|
||||
localStorage.setItem('jwt', token);
|
||||
};
|
||||
@@ -6,7 +10,8 @@ const getJWT = (): string | null => {
|
||||
return localStorage.getItem('jwt');
|
||||
};
|
||||
|
||||
export const storage = {
|
||||
export const StorageService = {
|
||||
clear,
|
||||
getJWT,
|
||||
setJWT,
|
||||
};
|
||||
Reference in New Issue
Block a user