Added Query entries

- changed id to type ID!
- changed id to md5 hash of base64 string of properties
- added discord Id
- added paging to arrays
- implemented guildHandler into Guild query type
- added saved to retrieve saved songs/playlists
- added ready indicator boolean
- added querying logs
- added presences array
- added config as String
- added prefix
pull/33/head
Trivernis 6 years ago
parent 275179b990
commit 72794a2f63

@ -4,7 +4,6 @@ const Discord = require("discord.js"),
cmd = require("./lib/cmd"), cmd = require("./lib/cmd"),
guilding = require('./lib/guilding'), guilding = require('./lib/guilding'),
utils = require('./lib/utils'), utils = require('./lib/utils'),
webapi = require('./lib/webapi'),
config = require('./config.json'), config = require('./config.json'),
args = require('args-parser')(process.argv), args = require('args-parser')(process.argv),
sqlite3 = require('sqlite3'), sqlite3 = require('sqlite3'),
@ -12,6 +11,8 @@ const Discord = require("discord.js"),
prefix = args.prefix || config.prefix || '~', prefix = args.prefix || config.prefix || '~',
gamepresence = args.game || config.presence; gamepresence = args.game || config.presence;
let webapi = null;
class Bot { class Bot {
constructor() { constructor() {
this.client = new Discord.Client(); this.client = new Discord.Client();
@ -44,11 +45,11 @@ class Bot {
} }
} }
guilding.setLogger(logger); guilding.setLogger(logger);
webapi.setLogger(logger);
cmd.init(prefix); cmd.init(prefix);
logger.verbose('Registering commands'); logger.verbose('Registering commands');
this.registerCommands(); this.registerCommands();
logger.debug('Checking for ./data/ existence'); logger.debug('Checking for ./data/ existence');
utils.dirExistence('./data', () => { utils.dirExistence('./data', () => {
logger.verbose('Connecting to main database'); logger.verbose('Connecting to main database');
this.maindb = new sqlite3.Database('./data/main.db', (err) => { this.maindb = new sqlite3.Database('./data/main.db', (err) => {
@ -58,7 +59,7 @@ class Bot {
this.maindb.run(`${utils.sql.tableExistCreate} presences ( this.maindb.run(`${utils.sql.tableExistCreate} presences (
${utils.sql.pkIdSerial}, ${utils.sql.pkIdSerial},
text VARCHAR(255) UNIQUE NOT NULL text VARCHAR(255) UNIQUE NOT NULL
)`, (err) => { )`, (err) => {
if (err) { if (err) {
logger.error(err.message); logger.error(err.message);
} else { } else {
@ -72,15 +73,27 @@ class Bot {
this.registerCallbacks(); this.registerCallbacks();
if (config.webservice && config.webservice.enabled) { if (config.webservice && config.webservice.enabled) {
logger.verbose('Importing webapi');
webapi = require('./lib/webapi');
webapi.setLogger(logger);
logger.verbose('Creating WebServer'); logger.verbose('Creating WebServer');
this.webServer = new webapi.WebServer(config.webservice.port || 8080); this.webServer = new webapi.WebServer(config.webservice.port || 8080);
logger.debug('Setting Reference Objects to webserver'); logger.debug('Setting Reference Objects to webserver');
this.webServer.setReferenceObjects({ this.webServer.setReferenceObjects({
client: this.client client: this.client,
presences: this.presences,
maind: this.maindb,
prefix: prefix,
getGuildHandler: (guild) => this.getGuildHandler(guild, prefix)
}); });
} }
} }
/**
* Starting the bot by connecting to the discord service and starting the webservice.
* @returns {Promise<any>}
*/
start() { start() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.client.login(authToken).then(() => { this.client.login(authToken).then(() => {
@ -89,7 +102,7 @@ class Bot {
}).catch((err) => { }).catch((err) => {
reject(err); reject(err);
}); });
if (this.webServer){ if (this.webServer) {
this.webServer.start(); this.webServer.start();
logger.info(`WebServer runing on port ${this.webServer.port}`); logger.info(`WebServer runing on port ${this.webServer.port}`);
} }
@ -116,7 +129,8 @@ class Bot {
}); });
this.presences.push(line); this.presences.push(line);
}); });
this.rotator = this.client.setInterval(() => this.rotatePresence(), config.presence_duration || 360000); this.rotator = this.client.setInterval(() => this.rotatePresence(),
config.presence_duration || 360000);
fs.unlink('./data/presences.txt', (err) => { fs.unlink('./data/presences.txt', (err) => {
if (err) if (err)
logger.warn(err.message); logger.warn(err.message);
@ -140,7 +154,8 @@ class Bot {
this.presences.push(row.text); this.presences.push(row.text);
} }
} }
this.rotator = this.client.setInterval(() => this.rotatePresence(), config.presence_duration || 360000); this.rotator = this.client.setInterval(() => this.rotatePresence(),
config.presence_duration || 360000);
}) })
} }
} }
@ -219,8 +234,10 @@ class Bot {
rotatePresence() { rotatePresence() {
let pr = this.presences.shift(); let pr = this.presences.shift();
this.presences.push(pr); this.presences.push(pr);
this.client.user.setPresence({game: {name: `${gamepresence} | ${pr}`, type: "PLAYING"}, status: 'online'}); this.client.user.setPresence({
logger.debug(`Presence rotation to ${pr}`); game: {name: `${gamepresence} | ${pr}`, type: "PLAYING"},
status: 'online'
}).then(() => logger.debug(`Presence rotation to ${pr}`));
} }
@ -234,7 +251,11 @@ class Bot {
this.client.on('ready', () => { this.client.on('ready', () => {
logger.info(`logged in as ${this.client.user.tag}!`); logger.info(`logged in as ${this.client.user.tag}!`);
this.client.user.setPresence({game: {name: gamepresence, type: "PLAYING"}, status: 'online'}) this.client.user.setPresence({
game: {
name: gamepresence, type: "PLAYING"
}, status: 'online'
})
.catch((err) => { .catch((err) => {
if (err) if (err)
logger.warn(err.message); logger.warn(err.message);

@ -1,39 +1,60 @@
type User { type User {
id: String id: ID!
name: String discordId: String
name: String!
avatar: String avatar: String
bot: Boolean bot: Boolean
tag: String tag: String!
} }
type Role { type Role {
id: String id: ID!
discordId: String
name: String name: String
color: String color: String
members: [GuildMember] members(first: Int = 10, offset: Int = 0, id: String): [GuildMember]
} }
type GuildMember { type GuildMember {
id: String id: ID!
discordId: String
user: User user: User
nickname: String nickname: String
roles: [Role] roles(first: Int = 10, offset: Int = 0, id: String): [Role]
highestRole: Role highestRole: Role
} }
type Guild { type Guild {
id: String id: ID!
discordId: String
name: String name: String
owner: GuildMember owner: GuildMember
members: [GuildMember] members(first: Int = 10, offset: Int = 0, id: String): [GuildMember]
roles: [Role] roles(first: Int = 10, offset: Int = 0, id: String): [Role]
memberCount: Int memberCount: Int
icon: String icon: String
ready: Boolean
saved(first: Int = 10, offset: Int = 0, id: String, name: String): [SavedEntry!]
} }
type Client { type Client {
guilds(count: Int): [Guild] guilds(first: Int = 10, offset: Int = 0, id: String): [Guild]
user: User user: User
ping: Float ping: Float
status: Int status: Int
uptime: Int uptime: Int
} }
type SavedEntry {
id: ID!
url: String!
name: String!
}
type LogEntry {
id: ID!
message: String
level: String
timestamp: String
}
type Query { type Query {
client: Client client: Client
presences: [String]!
config: String
prefix: String
logs(first: Int, offset: Int = 0, id: String, last: Int = 10): [LogEntry]
} }

@ -11,37 +11,38 @@ const winston = require('winston'),
loggingFullFormat = winston.format.combine( loggingFullFormat = winston.format.combine(
winston.format.splat(), winston.format.splat(),
winston.format.timestamp({ winston.format.timestamp({
format: 'MM-DD HH:mm:ss.SSS' // don't include the year because the filename already tells format: 'YY-MM-DD HH:mm:ss.SSS'
}), }),
fileLoggingFormat // the logging format for files that logs with a capitalized level winston.format.json()
), ),
logger = winston.createLogger({ logger = winston.createLogger({
level: winston.config.npm.levels, // logs with npm levels level: winston.config.npm.levels, // logs with npm levels
format: loggingFullFormat, // the full format for files format: loggingFullFormat,
transports: [ transports: [
new winston.transports.Console({ new winston.transports.Console({
format: winston.format.combine( format: winston.format.combine(
winston.format.colorize(), // colorizes the console logging output winston.format.colorize(),
winston.format.splat(), winston.format.splat(),
winston.format.timestamp({ winston.format.timestamp({
format: 'YY-MM-DD HH:mm:ss.SSS' // logs with the year to the console format: 'YY-MM-DD HH:mm:ss.SSS'
}), }),
consoleLoggingFormat // logs with the custom console format consoleLoggingFormat
), ),
level: args.loglevel || 'info' // logs to the console with the arg loglevel or info if it is not given level: args.loglevel || 'info'
}), }),
new winston.transports.File({ new winston.transports.File({
level: 'debug', // logs with debug level to the active file level: 'debug',
filename: './.log/latest.log', // the filename of the current file, filename: './.log/latest.log',
options: {flags: 'w'} // overwrites the file on restart options: {flags: 'w'} // overwrites the file on restart
}), }),
new DailyRotateFile({ new DailyRotateFile({
level: 'verbose', // log verbose in the rotating logvile level: 'verbose',
filename: './.log/%DATE%.log', // the pattern of the filename filename: './.log/%DATE%.log',
datePattern: 'YYYY-MM-DD', // the pattern of %DATE% datePattern: 'YYYY-MM-DD',
zippedArchive: true, // indicates that old logfiles should get zipped zippedArchive: true,
maxSize: '32m', // the maximum filesize maxSize: '32m',
maxFiles: '30d' // the maximum files to keep maxFiles: '30d',
json: true
}) })
] ]
}); });

@ -1,6 +1,8 @@
const express = require('express'), const express = require('express'),
graphqlHTTP = require('express-graphql'), graphqlHTTP = require('express-graphql'),
{buildSchema} = require('graphql'), {buildSchema} = require('graphql'),
compression = require('compression'),
md5 = require('js-md5'),
config = require('../config.json'), config = require('../config.json'),
fs = require('fs'); fs = require('fs');
@ -18,7 +20,19 @@ exports.WebServer = class {
this.root = {}; this.root = {};
} }
/**
* Starting the api webserver
*/
start() { start() {
this.app.use(compression({
filter: (req, res) => {
if (req.headers['x-no-compression']) {
return false
} else {
return compression.filter(req, res);
}
}
}));
this.app.use('/graphql', graphqlHTTP({ this.app.use('/graphql', graphqlHTTP({
schema: this.schema, schema: this.schema,
rootValue: this.root, rootValue: this.root,
@ -30,9 +44,17 @@ exports.WebServer = class {
setReferenceObjects(objects) { setReferenceObjects(objects) {
this.root = { this.root = {
client: { client: {
guilds: ({count}) => { guilds: (args) => {
let dcGuilds = objects.client.guilds.values(); let dcGuilds = objects.client.guilds.values();
return Array.from(dcGuilds).map((x) => new Guild(x)).slice(0, count); if (args.id) {
return [Array.from(dcGuilds)
.map((x) => new Guild(x, objects.getGuildHandler(x)))
.find(x => (x.id === args.id))];
} else {
return Array.from(dcGuilds)
.map((x) => new Guild(x, objects.getGuildHandler(x)))
.slice(args.offset, args.offset + args.first);
}
}, },
user: () => { user: () => {
return new User(objects.client.user); return new User(objects.client.user);
@ -45,54 +67,180 @@ exports.WebServer = class {
}, },
uptime: () => { uptime: () => {
return objects.client.uptime; return objects.client.uptime;
} },
},
prefix: objects.prefix,
presences: objects.presences,
config: JSON.stringify(config),
logs: (args) => {
return new Promise((resolve) => {
let logEntries = [];
let lineReader = require('readline').createInterface({
input: require('fs').createReadStream('./.log/latest.log')
});
lineReader.on('line', (line) => {
logEntries.push(new LogEntry(JSON.parse(line)));
});
lineReader.on('close', () => {
if (args.id) {
resolve([logEntries.find(x => (x.id === args.id))]);
} else if (args.first) {
resolve(logEntries.slice(args.offset, args.offset + args.first));
} else {
resolve(logEntries.slice(logEntries.length - args.last));
}
})
})
} }
} }
} }
}; };
function generateID(valArr) {
let b64 = Buffer.from(valArr.map(x => {
if (x)
return x.toString();
else
return 'null';
}).join('_')).toString('base64');
return md5(b64);
}
class Guild { class Guild {
constructor(discordGuild) { constructor(discordGuild, guildHandler) {
this.id = discordGuild.id; this.id = generateID(['Guild', discordGuild.id]);
this.discordId = discordGuild.id;
this.name = discordGuild.name; this.name = discordGuild.name;
this.owner = new GuildMember(discordGuild.owner); this.owner = new GuildMember(discordGuild.owner);
this.memberCount = discordGuild.memberCount; this.memberCount = discordGuild.memberCount;
this.icon = discordGuild.iconURL; this.icon = discordGuild.iconURL;
this.members = Array.from(discordGuild.members.values()) this.prMembers = Array.from(discordGuild.members.values())
.map((x) => new GuildMember(x)); .map((x) => new GuildMember(x));
this.roles = Array.from(discordGuild.roles.values()) this.prRoles = Array.from(discordGuild.roles.values())
.map((x) => new Role(x)); .map((x) => new Role(x));
guildHandler = guildHandler || {};
this.ready = guildHandler.ready;
this.prSaved = null;
this.guildHandler = guildHandler;
}
querySaved() {
return new Promise((resolve) => {
if (this.guildHandler.db) {
let saved = [];
this.guildHandler.db.all('SELECT * FROM playlists', (err, rows) => {
if (err) {
logger.error(err.message);
resolve(null)
} else {
for (let row of rows) {
saved.push({
id: generateID(['Guild', 'ROW', row.id, row.name]),
name: row.name,
url: row.url
});
}
resolve(saved);
}
})
} else {
resolve(null);
}
});
}
saved(args) {
return new Promise((resolve) => {
this.querySaved().then((result) => {
if (result) {
if (args.id) {
resolve([result.find(x => (x.id === args.id))]);
} else if (args.name) {
resolve([result.find(x => (x.name === args.name))]);
} else {
resolve(result.slice(args.offset, args.offset + args.first));
}
} else {
resolve(null);
}
})
})
}
roles(args) {
if (args.id) {
return [this.prRoles.find(x => (x.id === args.id))];
} else {
return this.prRoles.slice(args.offset, args.offset + args.first);
}
}
members(args) {
if (args.id) {
return [this.prMembers.find(x => (x.id === args.id))];
} else {
return this.prMembers.slice(args.offset, args.offset + args.first);
}
} }
} }
class Role { class Role {
constructor(discordRole) { constructor(discordRole) {
this.id = discordRole.id; this.id = generateID(['Role', discordRole.id]);
this.discordId = discordRole.id;
this.name = discordRole.name; this.name = discordRole.name;
this.color = discordRole.hexColor; this.color = discordRole.hexColor;
this.members = Array.from(discordRole.members.values) this.prMembers = Array.from(discordRole.members.values)
.map((x) => new GuildMember(x)); .map((x) => new GuildMember(x));
} }
members(args) {
if (args.id) {
return [this.prMembers.find(x => (x.id === args.id))];
} else {
return this.prMembers.slice(args.offset, args.offset + args.first);
}
}
} }
class GuildMember { class GuildMember {
constructor(discordGuildMember) { constructor(discordGuildMember) {
this.id = discordGuildMember.id; this.id = generateID(['GuildMember', discordGuildMember.id]);
this.discordId = discordGuildMember.id;
this.user = new User(discordGuildMember.user); this.user = new User(discordGuildMember.user);
this.nickname = discordGuildMember.nickname; this.nickname = discordGuildMember.nickname;
this.roles = Array.from(discordGuildMember.roles.values()) this.prRoles = Array.from(discordGuildMember.roles.values())
.map((x) => new Role(x)); .map((x) => new Role(x));
this.highestRole = new Role(discordGuildMember.highestRole); this.highestRole = new Role(discordGuildMember.highestRole);
} }
roles(args) {
if (args.id) {
return [this.prRoles.find(x => (x.id === args.id))];
} else {
return this.prRoles.slice(args.offset, args.offset + args.first);
}
}
} }
class User { class User {
constructor(discordUser) { constructor(discordUser) {
this.id = discordUser.id; this.id = generateID(['User', discordUser.id]);
this.discordId = discordUser.id;
this.name = discordUser.username; this.name = discordUser.username;
this.avatar = discordUser.avatarURL; this.avatar = discordUser.avatarURL;
this.bot = discordUser.bot; this.bot = discordUser.bot;
this.tag = discordUser.tag; this.tag = discordUser.tag;
this.tag = discordUser.tag; this.tag = discordUser.tag;
} }
}
class LogEntry {
constructor(entry) {
this.id = generateID(['LogEntry', entry.level, entry.timestamp]);
this.message = entry.message;
this.timestamp = entry.timestamp;
this.level = entry.level;
}
} }

@ -8,6 +8,7 @@
}, },
"dependencies": { "dependencies": {
"args-parser": "1.1.0", "args-parser": "1.1.0",
"compression": "^1.7.3",
"discord.js": "11.4.2", "discord.js": "11.4.2",
"eslint-plugin-graphql": "^3.0.1", "eslint-plugin-graphql": "^3.0.1",
"express": "^4.16.4", "express": "^4.16.4",
@ -15,6 +16,7 @@
"ffmpeg-binaries": "4.0.0", "ffmpeg-binaries": "4.0.0",
"get-youtube-title": "1.0.0", "get-youtube-title": "1.0.0",
"graphql": "^14.1.1", "graphql": "^14.1.1",
"js-md5": "^0.7.3",
"opusscript": "0.0.6", "opusscript": "0.0.6",
"sqlite3": "4.0.6", "sqlite3": "4.0.6",
"winston": "3.2.1", "winston": "3.2.1",

Loading…
Cancel
Save