forked from Galactic/modmail
canned replies, logs etc etc
This commit is contained in:
parent
05eb2ef0f1
commit
2a9218e7f3
@ -131,6 +131,21 @@ class ModmailClient extends Client {
|
||||
return this.resolver.resolveUser(input);
|
||||
}
|
||||
|
||||
async prompt(str, { author, channel, time }) {
|
||||
|
||||
if (!channel && author) channel = await author.createDM();
|
||||
if (!channel) throw new Error(`Missing channel for prompt, must pass at least author.`);
|
||||
await channel.send(str);
|
||||
return channel.awaitMessages((m) => m.author.id === author.id, { max: 1, time: time || 30000, errors: ['time'] })
|
||||
.then((collected) => {
|
||||
return collected.first();
|
||||
})
|
||||
.catch((error) => { //eslint-disable-line no-unused-vars, handle-callback-err
|
||||
return null;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ModmailClient;
|
@ -34,6 +34,7 @@ class Modmail {
|
||||
|
||||
// Sweep graveyard every 30 min and move stale channels to graveyard
|
||||
this.sweeper = setInterval(this.sweepChannels.bind(this), 5 * 60 * 1000);
|
||||
this.saver = setInterval(this.saveHistory.bind(this), 30 * 1000);
|
||||
|
||||
let logStr = `Started modmail handler for ${this.mainServer.name}`;
|
||||
if (this.bansServer) logStr += ` with ${this.bansServer.name} for ban appeals`;
|
||||
@ -125,8 +126,7 @@ class Modmail {
|
||||
if (!cache._channels) cache._channels = {};
|
||||
cache._channels[author.id] = channel;
|
||||
|
||||
this.mmcache[author.id] = pastModmail;
|
||||
this.mmcache[author.id].push({ author: author.id, content, timestamp: Date.now(), isReply: false });
|
||||
pastModmail.push({ author: author.id, content, timestamp: Date.now(), isReply: false });
|
||||
if (!this.updatedThreads.includes(author.id)) this.updatedThreads.push(author.id);
|
||||
|
||||
const embed = {
|
||||
@ -154,8 +154,6 @@ class Modmail {
|
||||
this.client.logger.error(`channel.send errored:\n${err.stack}\nContent: "${content}"`);
|
||||
});
|
||||
|
||||
if(!this.timeout || this.timeout._destroyed) this.timeout = setTimeout(this.saveHistory.bind(this), 30 * 1000);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -217,6 +215,10 @@ class Modmail {
|
||||
for (let i = context < len ? context : len; i > 0; i--) {
|
||||
const entry = history[len - i];
|
||||
if (!entry) continue;
|
||||
if (entry.markread) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const mem = entry.author.id === member.id ? member : this.mainServer.members.resolve(entry.author);
|
||||
|
||||
@ -350,12 +352,11 @@ class Modmail {
|
||||
this.client.logger.error(`Error during channel transition:\n${err.stack}`);
|
||||
});
|
||||
await message.delete().catch(this.client.logger.warn.bind(this.client.logger));
|
||||
if (!this.updatedThreads.includes(author.id)) this.updatedThreads.push(author.id);
|
||||
|
||||
}
|
||||
|
||||
async sendModmail({ message, content, anon, target }) {
|
||||
|
||||
console.log(content, anon, target.tag);
|
||||
|
||||
const targetMember = await this.getMember(target.id);
|
||||
if (!targetMember) return {
|
||||
@ -363,12 +364,12 @@ class Modmail {
|
||||
msg: `Cannot find member`
|
||||
};
|
||||
|
||||
const pastModmail = await this.loadHistory(target.id)
|
||||
const history = await this.loadHistory(target.id)
|
||||
.catch((err) => {
|
||||
this.client.logger.error(`Error during loading of past mail:\n${err.stack}`);
|
||||
return { error: true };
|
||||
});
|
||||
if (pastModmail.error) return {
|
||||
if (history.error) return {
|
||||
error: true,
|
||||
msg: `Internal error, this has been logged.`
|
||||
};
|
||||
@ -395,7 +396,9 @@ class Modmail {
|
||||
if (sent.error) return sent;
|
||||
|
||||
await message.channel.send('Delivered.').catch(this.client.logger.error.bind(this.client.logger));
|
||||
const channel = await this.loadChannel(targetMember, pastModmail).catch(this.client.logger.error.bind(this.client.logger));
|
||||
const channel = await this.loadChannel(targetMember, history).catch(this.client.logger.error.bind(this.client.logger));
|
||||
history.push({ author: member.id, content, timestamp: Date.now(), isReply: true, anon });
|
||||
if (!this.updatedThreads.includes(author.id)) this.updatedThreads.push(author.id);
|
||||
await channel.send({ embed }).catch(this.client.logger.error.bind(this.client.logger));
|
||||
await channel.edit({ parentID: this.readMail.id }).catch(this.client.logger.error.bind(this.client.logger));
|
||||
|
||||
@ -465,14 +468,37 @@ class Modmail {
|
||||
|
||||
async markread(message) {
|
||||
|
||||
const { channel } = message;
|
||||
const { channel, author } = message;
|
||||
|
||||
if (!this.categories.includes(channel.parentID)) return {
|
||||
error: true,
|
||||
msg: `This command only works in modmail channels.`
|
||||
};
|
||||
|
||||
const chCache = this.client.cache.channels;
|
||||
const result = Object.entries(chCache).find(([, val]) => {
|
||||
return val === channel.id;
|
||||
});
|
||||
|
||||
if (!result) return {
|
||||
error: true,
|
||||
msg: `This doesn't seem to be a valid modmail channel. Cache might be out of sync. **[MISSING TARGET]**`
|
||||
};
|
||||
|
||||
const [userId] = result;
|
||||
const history = await this.loadHistory(userId)
|
||||
.catch((err) => {
|
||||
this.client.logger.error(`Error during loading of past mail:\n${err.stack}`);
|
||||
return { error: true };
|
||||
});
|
||||
if (history.error) return {
|
||||
error: true,
|
||||
msg: `Internal error, this has been logged.`
|
||||
};
|
||||
history.push({ author: author.id, timestamp: Date.now(), markread: true }); // To keep track of read state
|
||||
|
||||
await channel.edit({ parentID: this.readMail.id });
|
||||
if (!this.updatedThreads.includes(author.id)) this.updatedThreads.push(userId);
|
||||
return `Done`;
|
||||
|
||||
}
|
||||
@ -488,7 +514,9 @@ class Modmail {
|
||||
|
||||
fs.readFile(path, { encoding: 'utf-8' }, (err, data) => {
|
||||
if (err) reject(err);
|
||||
resolve(JSON.parse(data));
|
||||
const parsed = JSON.parse(data);
|
||||
this.mmcache[userId] = parsed;
|
||||
resolve(parsed);
|
||||
});
|
||||
|
||||
});
|
||||
@ -515,11 +543,19 @@ class Modmail {
|
||||
|
||||
loadReplies() {
|
||||
|
||||
this.client.logger.info('Loading canned replies');
|
||||
if (!fs.existsSync('./canned_replies.json')) return {};
|
||||
return JSON.parse(fs.readFileSync('./canned_replies.json', { encoding: 'utf-8' }));
|
||||
|
||||
}
|
||||
|
||||
saveReplies() {
|
||||
|
||||
this.client.logger.info('Saving canned replies');
|
||||
fs.writeFileSync('./canned_replies.json', JSON.stringify(this.replies));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Modmail;
|
@ -15,17 +15,69 @@ class CannedReply extends Command {
|
||||
|
||||
const [first] = args.map((a) => a);
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { content, _caller } = message,
|
||||
let { channel, content, _caller } = message,
|
||||
anon = false;
|
||||
content = content.replace(`${this.client.prefix}${_caller}`, '');
|
||||
if (first.toLowerCase() === 'anon') {
|
||||
const op = args.shift().toLowerCase();
|
||||
if (op === 'anon') {
|
||||
anon = true;
|
||||
content = content.replace(first, '');
|
||||
} else if (['create', 'delete'].includes(op)) {
|
||||
return this.createCanned(op, args, message);
|
||||
} else if (['list'].includes(first.toLowerCase(op))) {
|
||||
|
||||
const list = Object.entries(this.client.modmail.replies);
|
||||
let str = '';
|
||||
for (const [name, content] of list) {
|
||||
if (str.length + content.length > 2000) {
|
||||
await channel.send(str).catch(this.client.logger.error.bind(this.client.logger));
|
||||
str = '';
|
||||
}
|
||||
str += `**${name}:** ${content}\n`;
|
||||
}
|
||||
if (str.length) await channel.send(str).catch(this.client.logger.error.bind(this.client.logger));
|
||||
return;
|
||||
}
|
||||
return this.client.modmail.sendCannedResponse({ message, responseName: content.trim(), anon });
|
||||
|
||||
}
|
||||
|
||||
async createCanned(op, args, { channel, author }) {
|
||||
|
||||
if (args.length < 1) return {
|
||||
error: true,
|
||||
msg: 'Missing reply name'
|
||||
};
|
||||
const [_name, ...rest] = args;
|
||||
|
||||
const name = _name.toLowerCase();
|
||||
const canned = this.client.modmail.replies;
|
||||
let confirmation = null;
|
||||
|
||||
if (op === 'create') {
|
||||
if (!rest.length) return {
|
||||
error: true,
|
||||
msg: 'Missing content'
|
||||
};
|
||||
|
||||
if (canned[name]) {
|
||||
confirmation = await this.client.prompt(`A canned reply by the name ${name} already exists, would you like to overwrite it?`, { channel, author });
|
||||
if (!confirmation) return 'Timed out.';
|
||||
confirmation = ['y', 'yes', 'ok'].includes(confirmation.content.toLowerCase());
|
||||
if (!confirmation) return 'Cancelled';
|
||||
}
|
||||
|
||||
canned[name] = rest.join(' ');
|
||||
|
||||
} else {
|
||||
delete canned[name];
|
||||
}
|
||||
|
||||
this.client.modmail.saveReplies();
|
||||
return `Updated ${_name}`;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = CannedReply;
|
71
structure/commands/Logs.js
Normal file
71
structure/commands/Logs.js
Normal file
@ -0,0 +1,71 @@
|
||||
const Command = require('../Command');
|
||||
|
||||
class Logs extends Command {
|
||||
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'logs',
|
||||
aliases: ['mmlogs', 'mmhistory'],
|
||||
showUsage: true,
|
||||
usage: '<user> [page]'
|
||||
});
|
||||
}
|
||||
|
||||
async execute(message, args) {
|
||||
|
||||
const user = await this.client.resolveUser(args[0]);
|
||||
let pageNr = 1;
|
||||
if (args[1]) {
|
||||
const num = parseInt(args[1]);
|
||||
if (isNaN(num)) return {
|
||||
error: true,
|
||||
msg: 'Invalid page number, must be number'
|
||||
};
|
||||
pageNr = num;
|
||||
}
|
||||
|
||||
const { member, channel } = message;
|
||||
const history = await this.client.modmail.loadHistory(user.id);
|
||||
const page = this.paginate([...history].reverse(), pageNr, 10);
|
||||
|
||||
const embed = {
|
||||
author: {
|
||||
name: `${user.tag} modmail history`,
|
||||
// eslint-disable-next-line camelcase
|
||||
icon_url: user.displayAvatarURL({ dynamic: true })
|
||||
},
|
||||
footer: {
|
||||
text: `${user.id} | Page ${page.page}/${page.maxPage}`
|
||||
},
|
||||
fields: [],
|
||||
color: member.highestRoleColor
|
||||
};
|
||||
|
||||
for (const entry of page.items) {
|
||||
const user = await this.client.resolveUser(entry.author);
|
||||
embed.fields.push({
|
||||
name: `${user.tag}${entry.anon ? ' (ANON)' : ''} @ ${new Date(entry.timestamp).toUTCString()}`,
|
||||
value: entry.content.substring(0, 1000) + (entry.content.length > 1000 ? '...' : '')
|
||||
});
|
||||
}
|
||||
|
||||
await channel.send({ embed });
|
||||
|
||||
}
|
||||
|
||||
paginate(items, page = 1, pageLength = 10) {
|
||||
const maxPage = Math.ceil(items.length / pageLength);
|
||||
if (page < 1) page = 1;
|
||||
if (page > maxPage) page = maxPage;
|
||||
const startIndex = (page - 1) * pageLength;
|
||||
return {
|
||||
items: items.length > pageLength ? items.slice(startIndex, startIndex + pageLength) : items,
|
||||
page,
|
||||
maxPage,
|
||||
pageLength
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Logs;
|
Loading…
Reference in New Issue
Block a user