lampselfbot/lampselfbot.js

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);