287 lines
13 KiB
JavaScript
287 lines
13 KiB
JavaScript
|
|
|
|
/* initialization */
|
|
|
|
process.on('unhandledRejection', error => {
|
|
console.error(error.stack || error);
|
|
});
|
|
|
|
var Discord = require('discord.js');
|
|
var fs = require('fs');
|
|
var child_process = require('child_process');
|
|
var exitHook = require('exit-hook');
|
|
|
|
var config = require('./config.json');
|
|
var client = new Discord.Client();
|
|
client.login(config.token);
|
|
client.on('ready', ()=>{
|
|
client.user.setStatus('invisible');
|
|
});
|
|
client.on('error', error => console.error(error.stack || error));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* logging */
|
|
|
|
var logpath = "discord.log";
|
|
fs.appendFileSync(logpath, '\n\n\n');
|
|
var log = {
|
|
log: function (str) {
|
|
var date = new Date();
|
|
var timestamp = date.toLocaleDateString() + " " + date.toLocaleTimeString() + " - ";
|
|
var line = timestamp + str;
|
|
line = line.replace(/\n/g, "\\n");
|
|
// console.log(line);
|
|
fs.appendFileSync(logpath, line + '\n');
|
|
},
|
|
channel: function (content, channel) {
|
|
//if (channel.guild && channel.guild.muted) return;
|
|
if (channel.guild)
|
|
this.log(`/${channel.guild.id}(${channel.guild.name})/${channel.id}(${channel.name})/${content}`);
|
|
else
|
|
this.log(`/${channel.id}(DM)/${content}`);
|
|
},
|
|
guild: function (content, guild) {
|
|
//if (guild.muted) return;
|
|
this.log(`/${guild.id}(${guild.name})/${content}`);
|
|
},
|
|
client: function (str) {
|
|
this.log(str);
|
|
}
|
|
};
|
|
log.client('Program Started');
|
|
|
|
// channel
|
|
client.on('message', (message) => log.channel(`${message.author.id}(${message.author.tag})/${message.id}: ${message.content} ${message.attachments.first() ? `\nAttachment: ${message.attachments.first().url}` : ''}`, message.channel));
|
|
client.on('messageDelete', (message) => log.channel(`Message ${message.id} has been deleted.`, message.channel));
|
|
client.on('messageDeleteBulk', (messages) => log.channel(`Messages have been bulk-deleted: ${messages.map(m => m.id).join(', ')}`, messages.first().channel));
|
|
client.on('messageUpdate', (oldMessage, newMessage) => {
|
|
if (newMessage.content !== oldMessage.content) log.channel(`Message ${oldMessage.id} has been edited.\nOld content: ${oldMessage.content}\nNew content: ${newMessage.content}`, newMessage.channel);
|
|
});
|
|
client.on('messageReactionAdd', (messageReaction, user) => log.channel(`User ${user.id} (${user.tag}) reacted to message ${messageReaction.message.id} with :${messageReaction.emoji.name}:`, messageReaction.message.channel));
|
|
client.on('messageReactionRemove', (messageReaction, user) => log.channel(`User ${user.id} (${user.tag}) removed reaction :${messageReaction.emoji.name}: from message ${messageReaction.message.id}`, messageReaction.message.channel));
|
|
client.on('messageReactionRemoveAll', (message) => log.channel(`All reactions on message ${message.id} have been removed.`, message.channel));
|
|
client.on('channelCreate', (channel) => log.channel(`Client gained access to channel.`, channel));
|
|
client.on('channelDelete', (channel) => log.channel(`Client lost access to channel.`, channel));
|
|
client.on('channelPinsUpdate', (channel) => log.channel(`Pinned messages have been updated.`, channel));
|
|
client.on('channelUpdate', (oldChannel, newChannel) => {
|
|
if (newChannel.type === "text") {
|
|
if (newChannel.name !== oldChannel.name) log.channel(`Channel renamed from #${oldChannel.name} to #${newChannel.name}`, newChannel);
|
|
if (newChannel.topic !== oldChannel.topic) log.channel(`Topic changed from "${oldChannel.topic}" to "${newChannel.topic}"`, newChannel);
|
|
if (newChannel.nsfw !== oldChannel.nsfw) log.channel(`NSFW mode is now ${newChannel.nsfw ? 'enabled' : 'disabled'}.`, newChannel);
|
|
if (newChannel.permissionOverwrites !== oldChannel.permissionOverwrites) log.channel(`Permissions have been updated.`, newChannel);
|
|
if (newChannel.position !== oldChannel.position) log.channel(`Channel position has changed to ${newChannel.position}`, newChannel);
|
|
}
|
|
if (newChannel.type === "voice") {
|
|
if (newChannel.name !== oldChannel.name) log.channel(`Channel name changed from "${oldChannel.name}" to "${newChannel.name}"`, newChannel);
|
|
if (newChannel.bitrate !== oldChannel.bitrate) log.channel(`Bitrate changed from "${oldChannel.bitrate}" to "${newChannel.bitrate}"`, newChannel)
|
|
if (newChannel.userLimit !== oldChannel.userLimit) log.channel(`User limit changed from "${oldChannel.userLimit}" to "${newChannel.userLimit}"`, newChannel);
|
|
if (newChannel.permissionOverwrites !== oldChannel.permissionOverwrites) log.channel(`Permissions have been updated.`, newChannel);
|
|
}
|
|
});
|
|
|
|
// guild
|
|
client.on('guildBanAdd', (guild, user) => log.guild(`User ${user.id} (${user.tag}) has been banned.`, guild));
|
|
client.on('guildBanRemove', (guild, user) => log.guild(`User ${user.id} (${user.tag}) has been unbanned.`, guild));
|
|
client.on('guildMemberAdd', (member) => log.guild(`User ${member.user.id} (${member.user.tag}) joined the guild.`, member.guild));
|
|
client.on('guildMemberRemove', (member) => log.guild(`User ${member.user.id} (${member.user.tag}) left the guild.`, member.guild));
|
|
client.on('guildMemberUpdate', (oldMember, newMember) => {
|
|
if (newMember.nickname !== oldMember.nickname) log.guild(`User ${newMember.user.id} (${newMember.user.tag}) changed their nickname from "${oldMember.nickname || '(none)'}" to "${newMember.nickname || '(none)'}"`, newMember.guild);
|
|
if (newMember.roles !== oldMember.roles) log.guild(`Roles on user ${newMember.user.id} (${newMember.user.tag}) have been updated.`, newMember.guild);
|
|
if (newMember.serverMute !== oldMember.serverMute) log.guild(`User ${newMember.user.id} (${newMember.user.tag}) has been ${newMember.serverMute ? '' : 'un-'}server-muted.`, newMember.guild);
|
|
if (newMember.serverDeaf !== oldMember.serverDeaf) log.guild(`User ${newMember.user.id} (${newMember.user.tag}) has been ${newMember.serverMute ? '' : 'un-'}server-deafened.`, newMember.guild);
|
|
});
|
|
client.on('voiceStateUpdate', (oldMember, newMember) => {
|
|
if (newMember.voiceChannel !== oldMember.voiceChannel) log.guild(`User ${newMember.user.id} (${newMember.user.tag}) ${newMember.voiceChannel ? `joined voice channel "${newMember.voiceChannel.name}"` : 'left voice channel.'}`, newMember.guild);
|
|
});
|
|
/*client.on('presenceUpdate', (oldMember, newMember)=>{
|
|
if (newMember.presence.status != oldMember.presence.status) log.guild(`User ${newMember.user.id} (${newMember.user.tag}) status changed from ${oldMember.presence.status} to ${newMember.presence.status}`, newMember.guild);
|
|
if ((newMember.presence.game && newMember.presence.game.name) != (oldMember.presence.game && oldMember.presence.game.name)) log.guild(`User ${newMember.user.id} (${newMember.user.tag}) is now playing ${newMember.presence.game && newMember.presence.game.name} after playing ${oldMember.presence.game && oldMember.presence.game.name}`, newMember.guild);
|
|
});*/
|
|
client.on('emojiCreate', (emoji) => log.guild(`Emoji "${emoji.identifier}" has been created. URL: ${emoji.url}`, emoji.guild));
|
|
client.on('emojiDelete', (emoji) => log.guild(`Emoji "${emoji.identifier}" has been deleted. URL: ${emoji.url}`, emoji.guild));
|
|
client.on('emojiUpdate', (oldEmoji, newEmoji) => {
|
|
if (newEmoji.name !== oldEmoji.name) log.guild(`Emoji "${oldEmoji.identifier}" has been renamed to ${newEmoji.name}`, newEmoji.guild);
|
|
});
|
|
client.on('roleCreate', role => log.guild(`Role "${role.name}" has been created. ID: ${role.id} Color: ${role.color} Permissions: ${role.permissions} Position: ${role.position}`, role.guild));
|
|
client.on('roleDelete', role => log.guild(`Role "${role.name}" has been deleted. ID: ${role.id} Color: ${role.color} Permissions: ${role.permissions} Position: ${role.position}`, role.guild));
|
|
client.on('roleUpdate', (oldRole, newRole) => {
|
|
if (newRole.name !== oldRole.name) log.guild(`Role ${oldRole.id} (${oldRole.name}) has been renamed to "${newRole.name}"`, newRole.guild);
|
|
if (newRole.color !== oldRole.color) log.guild(`Color of role ${oldRole.id} (${oldRole.name}) has changed from ${oldRole.color} to ${newRole.color}`, newRole.guild);
|
|
if (newRole.position !== oldRole.position) log.guild(`Position of role ${oldRole.id} (${oldRole.name}) changed from ${oldRole.position} to ${newRole.position}`, newRole.guild);
|
|
if (newRole.permissions !== oldRole.perissions) log.guild(`Permissions of role ${oldRole.id} (${oldRole.name}) changed from ${oldRole.permissions} to ${newRole.permissions}`, newRole.guild);
|
|
});
|
|
|
|
// client
|
|
client.on('guildCreate', (guild) => log.client(`Client joined guild ${guild.id} (${guild.name})`));
|
|
client.on('guildDelete', (guild) => log.client(`Client left guild ${guild.id} (${guild.name})`));
|
|
client.on('userUpdate', (oldUser, newUser) => {
|
|
if (newUser.tag !== oldUser.tag) log.client(`User ${newUser.id} changed their username from "${oldUser.tag}" to "${newUser.tag}"`);
|
|
if (newUser.displayAvatarURL !== oldUser.displayAvatarURL) log.client(`User ${newUser.id} (${newUser.tag}) changed their avatar. Old avatar: ${oldUser.displayAvatarURL} New avatar: ${newUser.displayAvatarURL}`);
|
|
});
|
|
|
|
client.on('ready', () => log.client('Client Ready'));
|
|
client.on('disconnect', (event) => log.client('Client Websocket Disconnected'));
|
|
client.on('reconnecting', () => log.client('Client Reconnecting to Websocket'));
|
|
client.on('resume', (replayed) => log.client(`Client Resumed. ${replayed}`));
|
|
client.on('warn', (info) => log.client(`WARN: ${info}`));
|
|
/*client.on('debug', (info) => log.client('Debug: '+info));*/
|
|
|
|
exitHook(() => log.client('Process Exiting'));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* self commands */
|
|
|
|
client.on('message', message => (async function(message){
|
|
if (message.author.id !== client.user.id) return;
|
|
|
|
var args = message.content.split(' ');
|
|
var txt = function(i) {return args.slice(i).join(' ');}
|
|
|
|
if (message.content.startsWith('~')) {
|
|
let cmd = args[0].slice(1).toLowerCase();
|
|
switch (cmd) {
|
|
|
|
case "eval":
|
|
case ">":
|
|
with (message) {
|
|
let output;
|
|
try { output = eval(txt(1)) }
|
|
catch (error) { output = error }
|
|
message.channel.send('`'+output+'`', {split:{char:''}});
|
|
}
|
|
break;
|
|
|
|
case "exec":
|
|
case "$":
|
|
child_process.exec(txt(1), function (error, stdout, stderr) {
|
|
if (error) message.channel.send(error);
|
|
if (stdout) message.channel.send(stdout, {split:{char:''}});
|
|
if (stderr) message.channel.send(stderr, {split:{char:''}});
|
|
});
|
|
break;
|
|
|
|
case "ping": message.channel.send('pong'); break;
|
|
|
|
case "randomemoji":
|
|
message.channel.send(client.emojis.random().toString());
|
|
break;
|
|
|
|
// experimental
|
|
case "record": {
|
|
let id = txt(1);
|
|
let vch = client.channels.get(id);
|
|
if (!(vch && vch.join)) message.react('⚠️');
|
|
else {
|
|
vch.join().then(vcon => {
|
|
var dp = "rec/" + new Date().toLocaleString();
|
|
fs.mkdirSync(dp);
|
|
let rcv = vcon.createReceiver();
|
|
rcv.userstreams = []; // y d.js no do dis
|
|
vcon.on("speaking", function(user, speaking){
|
|
if (!speaking) return;
|
|
for (let us of rcv.userstreams)
|
|
if (us.user == user)
|
|
return;
|
|
let stream = rcv.createStream(user, {mode:'pcm', end:'manual'});
|
|
rcv.userstreams.push({user, stream});
|
|
stream.pipe(fs.createWriteStream(`${dp}/${user.tag.replace(/\//g, ':')}.pcm`));
|
|
});
|
|
message.react('🆗');
|
|
});
|
|
}
|
|
}
|
|
break;
|
|
|
|
// experimental
|
|
case "stoprec": {
|
|
let id = txt(1);
|
|
let vch = client.channels.get(id);
|
|
if (!vch) message.react('⚠️');
|
|
else {
|
|
let vcon = vch.connection;
|
|
if (vcon) {
|
|
vcon.receivers.forEach(rcv => {
|
|
if (rcv.userstreams) {
|
|
rcv.userstreams.forEach(us => us.stream.end())
|
|
}
|
|
});
|
|
}
|
|
vch.leave();
|
|
message.react('🆗');
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
}).call(message, message));
|
|
|
|
|
|
|
|
/* fake nitro for solitary emoji */
|
|
|
|
client.on("message", async message => {
|
|
if (message.author == client.user && message.content.startsWith(':') && message.content.endsWith(':')) {
|
|
let name = message.content.slice(1, -1);
|
|
let emoji = client.emojis.find(x => x.name == name);
|
|
if (!emoji) return;
|
|
message.delete();
|
|
if (!fs.existsSync('.emojicache')) fs.mkdirSync('.emojicache');
|
|
let emoji_ext = emoji.url.split('.').pop();
|
|
let emoji_friendly_file_name = `${emoji.name}.${emoji_ext}`;
|
|
let cachedpath = `.emojicache/${emoji.url.split('/').pop()}`;
|
|
if (fs.existsSync(cachedpath)) {
|
|
await message.channel.send(new Discord.Attachment(cachedpath, emoji_friendly_file_name));
|
|
} else {
|
|
let emojibuf = (await require('snekfetch').get(emoji.url)).body;
|
|
let rszbuf;
|
|
if (emoji.animated) {
|
|
rszbuf = await new Promise(function(resolve,reject){
|
|
let cp = require("child_process").execFile(require("gifsicle"), ['-', '--resize-touch', '48x48'], {encoding: 'buffer', maxBuffer: 8*1024**2}, function(err,stdout,stderr){
|
|
if (err) return reject(err);
|
|
resolve(stdout);
|
|
});
|
|
cp.stdin.write(emojibuf);
|
|
cp.stdin.end();
|
|
});
|
|
} else {
|
|
rszbuf = await require('sharp')(emojibuf).resize(48,48,{fit:'inside'}).toBuffer();
|
|
}
|
|
await message.channel.send(new Discord.Attachment(rszbuf, emoji_friendly_file_name));
|
|
fs.writeFileSync(cachedpath, rszbuf);
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* fun little web server */
|
|
|
|
global.app = require('express')();
|
|
app.get("/emojis", async (req, res) => {
|
|
res.send(client.emojis.map(e=>`<img width="48px" height="48px" title="${e.name}" src="${e.url}" />`).join(''));
|
|
});
|
|
app.server = app.listen(3975);
|
|
|