pin functionality

This commit is contained in:
jane 2021-04-10 01:12:10 -04:00
parent b399aab2da
commit 047083d126
6 changed files with 307 additions and 44 deletions

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
config.json config.json
extset/
# ---> Node # ---> Node
# Logs # Logs

65
bot.js
View file

@ -14,6 +14,11 @@ if (!fs.existsSync('./whitelist.json')) {
} }
const whitelist = require('./whitelist.json'); const whitelist = require('./whitelist.json');
if (!fs.existsSync('./extset')) {
fs.mkdirSync('./extset');
}
const dir = dirname(import.meta.url); const dir = dirname(import.meta.url);
const bot = new Eris(cfg.token); const bot = new Eris(cfg.token);
@ -25,6 +30,45 @@ global.ctx = {
global.ctx[key] = value; global.ctx[key] = value;
}, },
}; };
let extension_settings = {};
const loadSettings = (guild) => {
extension_settings[guild] = {};
if (!fs.existsSync(`./extset/${guild}.json`)) {
fs.writeFileSync(`./extset/${guild}.json`, '{}');
}
let settings = require(`./extset/${guild}.json`);
if (settings != undefined) {
extension_settings[guild] = settings;
}
};
const saveSettings = (guild) => {
let data = JSON.stringify(extension_settings[guild]);
fs.writeFileSync(`./extset/${guild}.json`, data);
};
global.ctx.settings = {};
global.ctx.settings.getSetting = (guild, extensionId, key) => {
if (!extension_settings[guild]) {
loadSettings(guild);
}
if (!extension_settings[guild][extensionId]) {
extension_settings[guild][extensionId] = {};
}
return extension_settings[guild][extensionId][key];
};
global.ctx.settings.setSetting = (guild, extensionId, key, value) => {
if (!extension_settings[guild]) {
loadSettings(guild);
}
if (!extension_settings[guild][extensionId]) {
extension_settings[guild][extensionId] = {};
}
extension_settings[guild][extensionId][key] = value;
saveSettings(guild);
};
const log = new Logger(filename(import.meta.url), global.ctx.log_level); const log = new Logger(filename(import.meta.url), global.ctx.log_level);
const parse = new CommandParser(global.ctx, cfg.prefix || undefined); const parse = new CommandParser(global.ctx, cfg.prefix || undefined);
@ -55,7 +99,7 @@ global.ctx.whitelist = {
wl: whitelist, wl: whitelist,
}; };
const listeners = {} const listeners = {};
bot.on('ready', () => { bot.on('ready', () => {
log.info('ready recieved.'); log.info('ready recieved.');
@ -64,7 +108,7 @@ bot.on('ready', () => {
bot.on('messageCreate', (msg) => parse.parseMsg(msg, global.ctx)); bot.on('messageCreate', (msg) => parse.parseMsg(msg, global.ctx));
bot.on('error', (err) => log.error(err.toString())); bot.on('error', (err) => log.error(err.stack));
bot.connect(); bot.connect();
@ -83,6 +127,7 @@ async function load_commands() {
if (obj.default.constructor.name == 'CommandInitializer') { if (obj.default.constructor.name == 'CommandInitializer') {
obj.default.initialize(global.ctx); obj.default.initialize(global.ctx);
let cmds = obj.default.getCommands(); let cmds = obj.default.getCommands();
let aliases = obj.default.getAliases();
if (parse.isCmd(cmds)) { if (parse.isCmd(cmds)) {
parse.addCommand(cmds); parse.addCommand(cmds);
} else if (cmds.constructor.name == 'Array') { } else if (cmds.constructor.name == 'Array') {
@ -90,13 +135,25 @@ async function load_commands() {
parse.addCommand(cmd); parse.addCommand(cmd);
} }
} }
if (aliases != undefined) {
parse.addAliases(aliases);
}
let events = obj.default.getEvents(); let events = obj.default.getEvents();
if (events != undefined) { if (events != undefined) {
for (let event in events) { for (let event in events) {
if (!listeners[event]) { if (!listeners[event]) {
listeners[event] = [] listeners[event] = [];
} }
listeners[event].push(events[event]) log.trace(events[event]);
listeners[event] = listeners[event].concat(events[event]);
log.debug(`added ${events[event].length} events for ${event} from ${file}`);
bot.on(event, (...args) => {
for (let handler of listeners[event]) {
log.trace(event);
log.trace(handler);
handler.func(global.ctx, args);
}
});
} }
} }
} }

124
cmd/pin.js Normal file
View file

@ -0,0 +1,124 @@
import { CommandInitializer, Command, Event } from '../parser.js';
import Logger, { levels } from '../logger.js';
import { filename } from '../utils.js';
const initializer = new CommandInitializer();
const log = new Logger(filename(import.meta.url), global.ctx.log_level);
let activePins = {};
let defaultSettings = { threshold: 3, emoji: '📌' };
class PinEvent extends Event {
event = 'messageReactionAdd';
async func(ctx, args) {
const msg = args[0];
const fullMsg = await ctx.bot.getMessage(msg.channel.id, msg.id);
const reaction = args[1];
const reactor = args[2];
log.trace(msg);
log.trace(reaction);
log.trace(reactor);
let threshold = ctx.settings.getSetting(msg.channel.guild.id, 'pin', 'threshold');
if (threshold == undefined) {
threshold = defaultSettings.threshold;
ctx.settings.setSetting(msg.channel.guild.id, 'pin', 'threshold', threshold);
}
let emoji = ctx.settings.getSetting(msg.channel.guild.id, 'pin', 'emoji');
if (emoji == undefined) {
emoji = defaultSettings.emoji;
ctx.settings.setSetting(msg.channel.guild.id, 'pin', 'emoji', emoji);
}
if (reaction.name === emoji) {
const fullReaction = await ctx.bot.getMessageReaction(msg.channel.id, msg.id, emoji);
log.debug('reaction matched');
log.trace(fullReaction);
if (fullReaction.length >= threshold) {
if (fullMsg.author.bot || fullReaction.filter((reaction) => reaction.id == fullMsg.author.id).length > 0) {
if (activePins[fullMsg.id]) {
activePins[fullMsg.id].delete();
}
fullMsg.pin().catch(async (err) => {
let sn = await fullMsg.channel.createMessage('could not pin the message: ' + err.toString());
if (err.toString().includes('Missing Permissions')) {
sn.edit(
'could not pin the message: ' +
err.toString() +
'\n(requires the manage messages or pin messages permission)'
);
}
});
} else {
let text = '';
text += fullMsg.author.mention + ', ';
if (fullReaction.length == 1) {
text += fullReaction[0].username + ' wants ';
} else {
for (let i = 0; i < fullReaction.length; i++) {
text += (i == fullReaction.length - 1) ? 'and ' + fullReaction[i].username : fullReaction[i].username + ', ';
}
text += ' want ';
}
text += 'to pin your message. react with ' + emoji + ' to pin it.';
let message = await fullMsg.channel.createMessage(text);
activePins[fullMsg.id] = message;
}
}
} else if (emoji.contains(reaction.name) && reaction.emoji(reaction.id)) {
// good luck
}
}
}
initializer.addEvent(new PinEvent());
function settingsSubcommand(msg, args, ctx) {
if (args[1] == undefined) {
msg.channel.createMessage('not enough arguments given');
return;
}
if (defaultSettings[args[1].trim()] == undefined) {
msg.channel.createMessage(
args[1] +
' is not a valid setting.\navailable settings are:\n```\n' +
Object.keys(defaultSettings).join('\n') +
'\n```'
);
return;
}
if (args[2] == undefined) {
let setting = ctx.settings.getSetting(msg.channel.guild.id, 'pin', args[1]);
if (setting == undefined) {
setting = defaultSettings[args[1]];
ctx.settings.setSetting(msg.channel.guild.id, 'pin', args[1], defaultSettings[args[1]]);
}
msg.channel.createMessage(args[1] + ' is currently set to ' + setting);
} else {
ctx.settings.setSetting(msg.channel.guild.id, 'pin', args[1], args[2]);
msg.channel.createMessage('set the value of ' + args[1] + ' to ' + args[2]);
}
}
class PinCommand extends Command {
name = 'pin';
perms = 32;
description = 'manage the pin module';
helptext = '';
func(msg, args, ctx) {
switch (args[0]) {
case 'settings':
case 'setting':
settingsSubcommand(msg, args, ctx);
break;
case undefined:
msg.channel.createMessage('no arguments given');
break;
default:
msg.channel.createMessage('unknown argument ' + args[0]);
}
}
}
initializer.addCommand(new PinCommand());
export default initializer;

View file

@ -7,12 +7,13 @@ const log = new Logger(filename(import.meta.url), global.ctx.log_level);
class PingCommand extends Command { class PingCommand extends Command {
name = 'ping'; name = 'ping';
aliases = ['p'];
func(msg, args, ctx) { func(msg, args, ctx) {
msg.channel.createMessage('p').then((m) => { msg.channel.createMessage('p').then((m) => {
m.edit( m.edit(
`rtt: ${Math.floor(m.timestamp - msg.timestamp)}, gateway: ${ `rtt: ${Math.floor(m.timestamp - msg.timestamp)}ms, gateway: ${
ctx.bot.shards.get(ctx.bot.guildShardMap[ctx.bot.channelGuildMap[msg.channel.id]] || 0).latency ctx.bot.shards.get(ctx.bot.guildShardMap[ctx.bot.channelGuildMap[msg.channel.id]] || 0).latency
}` }ms`
); );
}); });
} }
@ -36,17 +37,30 @@ class EvalCommand extends Command {
whitelist = true; whitelist = true;
func(msg, args, ctx) { func(msg, args, ctx) {
log.debug('evaluating ' + args[0]); log.debug('evaluating ' + args[0]);
let result = eval(args[0]); let result;
log.debug('result is: \n' + result); try {
if (result.length <= 1999) { result = eval(args[0]);
msg.channel.createMessage(result); } catch (e) {
result = e.stack;
} }
else { log.debug('result is: \n' + result);
msg.channel.createMessage("result too long, see logs"); if (String.valueOf(result).length <= 1999) {
msg.channel.createMessage('```\n' + result + '```\n');
} else {
msg.channel.createMessage('result too long, see logs');
} }
} }
} }
initializer.addCommand(new EvalCommand()); initializer.addCommand(new EvalCommand());
class HelpCommand extends Command {
name = 'help';
description = 'get help for commands';
helptext = '```md\nhelp\nshows this text\n\nhelp <command>\nshows the helptext for <command>';
func(msg, args, ctx) {}
}
initializer.addCommand(new HelpCommand());
export default initializer; export default initializer;

View file

@ -1,5 +1,5 @@
import Logger, { levels } from './logger.js'; import Logger, { levels } from './logger.js';
import { filename } from './utils.js'; import { filename, getPerms, hasPerms } from './utils.js';
export class Command { export class Command {
init(ctx, log) { init(ctx, log) {
@ -7,17 +7,31 @@ export class Command {
} }
log; log;
name = 'DEFAULT'; name = 'DEFAULT';
description = 'No description';
helptext = 'No helptext';
aliases = '';
whitelist = false; whitelist = false;
perms = 0;
func() {} func() {}
} }
export class Event {
event = 'NONE';
func(...args) {}
}
export class CommandInitializer { export class CommandInitializer {
ctx;
log;
commands = []; commands = [];
aliases = {};
uninitialized = []; uninitialized = [];
events = {}; events = {};
initialize(ctx) { initialize(ctx) {
for (let index in this.uninitialized) { this.ctx = ctx;
this.log = new Logger('parser', ctx.log_level);
for (const index in this.uninitialized) {
this.initCommand(this.uninitialized[index], ctx); this.initCommand(this.uninitialized[index], ctx);
delete this.uninitialized[index]; delete this.uninitialized[index];
} }
@ -26,24 +40,34 @@ export class CommandInitializer {
initCommand(cmd, ctx) { initCommand(cmd, ctx) {
cmd.init(ctx, new Logger(`cmd.${cmd.name}`, ctx.log_level)); cmd.init(ctx, new Logger(`cmd.${cmd.name}`, ctx.log_level));
this.commands.push(cmd); this.commands.push(cmd);
for (const alias of cmd.aliases) {
this.aliases[alias] = cmd.name;
}
} }
addCommand(cmd) { addCommand(cmd) {
this.uninitialized.push(cmd); this.uninitialized.push(cmd);
} }
addEvent(event, func) { addEvent(handler) {
if (!events[event]) { console.log(handler);
events[event] = []; const event = handler.event;
if (!this.events[event]) {
this.events[event] = [];
} }
events[event].push(func); this.events[event].push(handler);
} }
getCommands() { getCommands() {
return this.commands; return this.commands;
} }
getAliases() {
return this.aliases;
}
getEvents() { getEvents() {
console.log(this.events);
return this.events; return this.events;
} }
} }
@ -56,6 +80,7 @@ export default class CommandParser {
log; log;
prefix; prefix;
commands = []; commands = [];
aliases = {};
addCommand(cmd) { addCommand(cmd) {
this.log.trace(`cmd to add: ${JSON.stringify(cmd)}`); this.log.trace(`cmd to add: ${JSON.stringify(cmd)}`);
@ -64,8 +89,20 @@ export default class CommandParser {
} }
} }
addAliases(aliases) {
this.log.debug('added ' + Object.keys(aliases).length + ' aliases');
this.aliases = {
...this.aliases,
...aliases,
};
}
hasCmd(str) { hasCmd(str) {
let results = this.commands.filter((c) => c.name == str); if (this.aliases && this.aliases[str] != undefined) {
str = this.aliases[str];
}
const results = this.commands.filter((c) => c.name == str);
return results.length ? results[0] : undefined; return results.length ? results[0] : undefined;
} }
@ -75,7 +112,7 @@ export default class CommandParser {
} }
getArgsList(split) { getArgsList(split) {
let parsed_args = []; const parsed_args = [];
let join_index = -1; let join_index = -1;
let add = true; let add = true;
for (let index in split) { for (let index in split) {
@ -112,18 +149,29 @@ export default class CommandParser {
this.log.trace(msg.content[0]); this.log.trace(msg.content[0]);
this.log.trace(this.prefix); this.log.trace(this.prefix);
if (msg.content.startsWith(this.prefix)) { if (msg.content.startsWith(this.prefix)) {
let snip = msg.content.slice(this.prefix.length); const snip = msg.content.slice(this.prefix.length);
let unsep = snip.split(' '); const unsep = snip.split(' ');
let res = this.hasCmd(unsep[0]); const res = this.hasCmd(unsep[0]);
let args = this.getArgsList(unsep.slice(1)); const args = this.getArgsList(unsep.slice(1));
this.log.trace(snip); this.log.trace(snip);
this.log.trace(res); this.log.trace(res);
this.log.trace(args); this.log.trace(args);
if (res != undefined) { if (res != undefined) {
this.log.debug(`execute function ${res.name}`); this.log.debug(`execute function ${res.name}`);
const perms = msg.channel.guild.members.get(msg.author.id).permissions;
const resolvedPerms = res.perms.toString();
this.log.trace(resolvedPerms);
this.log.trace(perms);
if (res.whitelist && !ctx.whitelist.user(msg.author)) { if (res.whitelist && !ctx.whitelist.user(msg.author)) {
msg.channel.createMessage('not whitelisted'); msg.channel.createMessage('not whitelisted');
} else if (resolvedPerms && !hasPerms(perms, resolvedPerms)) {
const required = getPerms(perms.allow.toString() & res.perms).join(', ');
msg.channel.createMessage('not enough permissions.').then((sent) => {
if (required.length <= 1000) {
sent.edit('not enough permissions. needs: `' + required + '`');
}
});
} else { } else {
res.func(msg, args, ctx); res.func(msg, args, ctx);
} }

View file

@ -1,15 +1,34 @@
import path from "path"; import path from 'path';
import {platform} from "process"; import { platform } from 'process';
import { Constants } from 'eris';
export function filename(url) { export function filename(url) {
let __filename = new URL(url).pathname; const __filename = new URL(url).pathname;
return path.basename(__filename, ".js"); return path.basename(__filename, '.js');
} }
export function dirname(url) { export function dirname(url) {
let __filename = new URL(url).pathname; const __filename = new URL(url).pathname;
let __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
return platform == "win32" return platform == 'win32' ? __dirname.slice(1) : __dirname;
? __dirname.slice(1) }
: __dirname;
export function getPerms(perms) {
const result = [];
for (const key in Constants.Permissions) {
if (Constants.Permissions[key].toString() & perms) {
result.push(key);
}
}
return result;
}
export function hasPerms(perms, test) {
let testPerms = getPerms(test);
for (const tp of testPerms) {
if (!perms.has(tp)) {
return false;
}
}
return true;
} }