From b50215404cbf048a74c86941908e003c0e1a01b4 Mon Sep 17 00:00:00 2001 From: "Isaac (eartharoid)" Date: Sat, 22 Aug 2020 22:40:27 +0100 Subject: [PATCH] progress. archiving works (other than uploading to server) --- README.md | 2 + src/commands/close.js | 132 ++++-- src/commands/new.js | 2 +- src/commands/panel.js | 3 +- src/commands/stats.js | 19 +- src/commands/topic.js | 24 ++ src/commands/transcript.js | 5 +- src/events/message.js | 4 +- src/events/messageDelete.js | 3 +- src/events/messageReactionAdd.js | 2 +- src/events/messageUpdate.js | 24 +- src/utils/archive.js | 95 +++-- user/config.js | 2 +- user/transcripts/.json | 10 - user/transcripts/example.json | 661 ------------------------------- 15 files changed, 223 insertions(+), 765 deletions(-) create mode 100644 src/commands/topic.js delete mode 100644 user/transcripts/.json delete mode 100644 user/transcripts/example.json diff --git a/README.md b/README.md index 907fda3..8e3b819 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,6 @@ A simple Discord bot to manage support ticket channels to allow you and your tea **Go to the [wiki](https://github.com/Eartharoid/DiscordTickets/wiki) for more information** +https://discord.com/api/oauth2/authorize?client_id=INSERT-YOUR-BOT-ID-HERE&permissions=8&scope=bot + # REWRITE IN PROGRESS, PLEASE DON'T DOWNLOAD FROM MASTER, DOWNLOAD [LATEST RELEASE](https://github.com/eartharoid/DiscordTickets/releases) OR WAIT A FEW DAYS. diff --git a/src/commands/close.js b/src/commands/close.js index cc3cf5d..010eb47 100644 --- a/src/commands/close.js +++ b/src/commands/close.js @@ -11,6 +11,7 @@ const log = new ChildLogger(); const { MessageEmbed } = require('discord.js'); const config = require('../../user/config'); const fs = require('fs'); +const archive = require('../utils/archive'); module.exports = { name: 'close', @@ -22,7 +23,7 @@ module.exports = { async execute(client, message, args, Ticket) { const guild = client.guilds.cache.get(config.guild); - + const notTicket = new MessageEmbed() .setColor(config.err_colour) .setAuthor(message.author.username, message.author.displayAvatarURL()) @@ -31,29 +32,37 @@ module.exports = { .addField('Usage', `\`${config.prefix}${this.name} ${this.usage}\`\n`) .addField('Help', `Type \`${config.prefix}help ${this.name}\` for more information`) .setFooter(guild.name, guild.iconURL()); - + let ticket; let channel = message.mentions.channels.first(); // || client.channels.resolve(await Ticket.findOne({ where: { id: args[0] } }).channel) // channels.fetch() - if(!channel) { + if (!channel) { channel = message.channel; - ticket = await Ticket.findOne({ where: { channel: channel.id } }); - if(!ticket) + ticket = await Ticket.findOne({ + where: { + channel: channel.id + } + }); + if (!ticket) return channel.send(notTicket); } else { - ticket = await Ticket.findOne({ where: { channel: channel.id } }); - if(!ticket) { + ticket = await Ticket.findOne({ + where: { + channel: channel.id + } + }); + if (!ticket) { notTicket .setTitle(':x: **Channel is not a ticket**') .setDescription(`${channel} is not a ticket channel.`); return channel.send(notTicket); } - if(message.author.id !== ticket.get('creator') && !message.member.roles.cache.has(config.staff_role)) + if (message.author.id !== ticket.get('creator') && !message.member.roles.cache.has(config.staff_role)) return channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -67,10 +76,10 @@ module.exports = { } let success; - let pre = fs.existsSync(`user/transcripts/text/${channel.id}.txt`) - || fs.existsSync(`user/transcripts/raw/${channel.id}.log`) ? - `You will be able to view an archived version later with \`${config.prefix}transcript ${ticket.get('id')}\`` - : ''; + let pre = fs.existsSync(`user/transcripts/text/${channel.id}.txt`) || + fs.existsSync(`user/transcripts/raw/${channel.id}.log`) ? + `You will be able to view an archived version later with \`${config.prefix}transcript ${ticket.get('id')}\`` : + ''; let confirm = await message.channel.send( new MessageEmbed() @@ -82,12 +91,13 @@ module.exports = { ); await confirm.react('✅'); - + const collector = confirm.createReactionCollector( - (r, u) => r.emoji.name === '✅' && u.id === message.author.id, - { time: 15000 }); - - collector.on('collect', () => { + (r, u) => r.emoji.name === '✅' && u.id === message.author.id, { + time: 15000 + }); + + collector.on('collect', async () => { if (channel.id !== message.channel.id) channel.send( new MessageEmbed() @@ -109,32 +119,94 @@ module.exports = { ); success = true; - ticket.update({ open: false}, { where: { channel: channel.id } }); + ticket.update({ + open: false + }, { + where: { + channel: channel.id + } + }); setTimeout(() => { channel.delete(); - if (channel.id !== message.channel.id) + if (channel.id !== message.channel.id) message.delete() .then(() => confirm.delete()); }, 15000); + if (config.transcripts.text.enabled || config.transcripts.web.enabled) { + let u = await client.users.fetch(ticket.get('creator')); + + if (u) { + let dm; + try { + dm = u.dmChannel || await u.createDM(); + } catch (e) { + log.warn(`Could not create DM channel with ${u.tag}`); + } + + + await dm.send( + new MessageEmbed() + .setColor(config.colour) + .setAuthor(message.author.username, message.author.displayAvatarURL()) + .setTitle(`**Ticket ${ticket.id} closed**`) + .setDescription('Your ticket has been closed.') + .setFooter(guild.name, guild.iconURL()) + ); + + if (config.transcripts.text.enabled && fs.existsSync(`user/transcripts/text/${channel.id}.txt`)) { + try { + await dm.send('A basic text transcript of the ticket channel is attached:', { + files: [ + `user/transcripts/text/${channel.id}.txt` + ] + }); + } catch (e) { + log.warn(`Failed to send text transcript to ${u.tag}`); + } + + } + + if (config.transcripts.web.enabled) { + try { + let url = await archive.export(client, channel); + + await dm.send( + new MessageEmbed() + .setColor(config.colour) + .setAuthor(message.author.username, message.author.displayAvatarURL()) + .setTitle(`**Ticket ${ticket.id} web archive**`) + .setDescription(`You can view an archive of your ticket channel [here](${url})`) + .setFooter(guild.name, guild.iconURL()) + ); + } catch (e) { + log.warn(`Failed to send archive URL to ${u.tag}`); + log.warn(e); + } + + } + } + } + + log.info(`${message.author.tag} closed a ticket (#ticket-${ticket.get('id')})`); - + if (config.logs.discord.enabled) client.channels.cache.get(config.logs.discord.channel).send( new MessageEmbed() .setColor(config.colour) .setAuthor(message.author.username, message.author.displayAvatarURL()) .setTitle('Ticket closed') - .addField('Creator', `<@${ticket.get('creator')}>` , true) + .addField('Creator', `<@${ticket.get('creator')}>`, true) .addField('Closed by', message.author, true) .setFooter(guild.name, guild.iconURL()) .setTimestamp() ); }); - - + + collector.on('end', () => { - if(!success) { + if (!success) { confirm.reactions.removeAll(); confirm.edit( new MessageEmbed() @@ -143,11 +215,13 @@ module.exports = { .setTitle(':x: **Expired**') .setDescription('You took to long to react; confirmation failed.') .setFooter(guild.name, guild.iconURL())); - - message.delete({ timeout: 10000 }) + + message.delete({ + timeout: 10000 + }) .then(() => confirm.delete()); - } + } }); - + } -}; +}; \ No newline at end of file diff --git a/src/commands/new.js b/src/commands/new.js index 849882c..bba5420 100644 --- a/src/commands/new.js +++ b/src/commands/new.js @@ -132,7 +132,7 @@ module.exports = { await m.delete(); }, 15000); - require('../utils/archive').create(client, c); // create files + // require('../utils/archive').create(client, c); // create files let ping; switch (config.tickets.ping) { diff --git a/src/commands/panel.js b/src/commands/panel.js index 8e59577..595ac14 100644 --- a/src/commands/panel.js +++ b/src/commands/panel.js @@ -41,7 +41,7 @@ module.exports = { } else { panel = await client.channels.cache.get(chanID.get('value')).messages.fetch(msgID.get('value')); // get old panel message if (panel) - panel.delete({ reason: 'Creating new panel/widget' }).then(() => log.info('Deleted old panel')); // delete old panel + panel.delete({ reason: 'Creating new panel/widget' }).then(() => log.info('Deleted old panel')).catch(e => log.warn(e)); // delete old panel } message.delete(); @@ -55,6 +55,7 @@ module.exports = { ); // send new panel panel.react(config.panel.reaction); // add reaction + Setting.update({ value: message.channel.id }, { where: { key: 'panel_chan_id' }}); // update database Setting.update({ value: panel.id }, { where: { key: 'panel_msg_id' }}); // update database log.info(`${message.author.tag} created a panel widget`); diff --git a/src/commands/stats.js b/src/commands/stats.js index 2b14a35..1e3d8bc 100644 --- a/src/commands/stats.js +++ b/src/commands/stats.js @@ -8,16 +8,31 @@ const ChildLogger = require('leekslazylogger').ChildLogger; const log = new ChildLogger(); -const Discord = require('discord.js'); +const { MessageEmbed } = require('discord.js'); const config = require('../../user/config'); module.exports = { name: 'stats', description: 'View ticket stats.', usage: '', - aliases: ['data'], + aliases: ['data', 'statistics'], example: '', args: false, async execute(client, message, args, Ticket) { + + const guild = client.guilds.cache.get(config.guild); + + let open = await Ticket.count({ where: { open: true } }); + let closed = await Ticket.count({ where: { open: false } }); + + message.channel.send( + new MessageEmbed() + .setColor(config.colour) + .setTitle(':bar_chart: Statistics') + .addField('Open tickets', open, true) + .addField('Closed tickets', closed, true) + .addField('Total tickets', open + closed, true) + .setFooter(guild.name, guild.iconURL()) + ); } }; \ No newline at end of file diff --git a/src/commands/topic.js b/src/commands/topic.js new file mode 100644 index 0000000..8b57b30 --- /dev/null +++ b/src/commands/topic.js @@ -0,0 +1,24 @@ +/** + * + * @name DiscordTickets + * @author eartharoid + * @license GNU-GPLv3 + * + */ + +const ChildLogger = require('leekslazylogger').ChildLogger; +const log = new ChildLogger(); +const { MessageEmbed } = require('discord.js'); +const config = require('../../user/config'); + +module.exports = { + name: 'topic', + description: 'Edit a ticket topic', + usage: '', + aliases: ['edit'], + example: 'topic need help error', + args: false, + async execute(client, message, args, Ticket) { + + } +}; \ No newline at end of file diff --git a/src/commands/transcript.js b/src/commands/transcript.js index f950e92..d452450 100644 --- a/src/commands/transcript.js +++ b/src/commands/transcript.js @@ -6,8 +6,9 @@ * */ -const Discord = require('discord.js'); -const fs = require('fs'); +const ChildLogger = require('leekslazylogger').ChildLogger; +const log = new ChildLogger(); +const { MessageEmbed } = require('discord.js'); const config = require('../../user/config'); module.exports = { diff --git a/src/events/message.js b/src/events/message.js index 4d41e8e..42cb941 100644 --- a/src/events/message.js +++ b/src/events/message.js @@ -32,9 +32,9 @@ Type \`${config.prefix}new\` on the server to create a new ticket.`); let ticket = await Ticket.findOne({ where: { channel: message.channel.id } }); if(ticket) - archive.addMessage(client, message); + archive.add(client, message); // add message to archive - if (message.author.bot || message.author.id === client.user.id) return; // if bot, fuck off + if (message.author.bot || message.author.id === client.user.id) return; // goodbye bots /** diff --git a/src/events/messageDelete.js b/src/events/messageDelete.js index 893d966..1e85889 100644 --- a/src/events/messageDelete.js +++ b/src/events/messageDelete.js @@ -43,7 +43,8 @@ module.exports = { time: message.createdTimestamp, embeds: embeds, attachments: [...message.attachments.values()], - deleted: true + edited: message.edits.length > 1, + deleted: true // delete the message }) + '\n'); } diff --git a/src/events/messageReactionAdd.js b/src/events/messageReactionAdd.js index e77a1c4..fbc1aa9 100644 --- a/src/events/messageReactionAdd.js +++ b/src/events/messageReactionAdd.js @@ -134,7 +134,7 @@ module.exports = { } }); - require('../utils/archive').create(client, c); // create files + // require('../utils/archive').create(client, c); // create files let ping; switch (config.tickets.ping) { diff --git a/src/events/messageUpdate.js b/src/events/messageUpdate.js index f765e7b..25c1c85 100644 --- a/src/events/messageUpdate.js +++ b/src/events/messageUpdate.js @@ -14,7 +14,7 @@ const fs = require('fs'); const dtf = require('@eartharoid/dtf'); module.exports = { - event: 'oUpdate', + event: 'messageUpdate', async execute(client, [o, n], {Ticket}) { if(!config.transcripts.web.enabled) return; @@ -29,29 +29,29 @@ module.exports = { if (n.partial) try { - await o.fetch(); + await n.fetch(); } catch (err) { log.error(err); return; } - if(o === n) return; + if(o.content === n.content) return; // apparently editing a message isn't the only thing that emits this event - let ticket = await Ticket.findOne({ where: { channel: o.channel.id } }); + let ticket = await Ticket.findOne({ where: { channel: n.channel.id } }); if(!ticket) return; - let path = `user/transcripts/raw/${o.channel.id}.log`; + let path = `user/transcripts/raw/${n.channel.id}.log`; let embeds = []; - for (let embed in o.embeds) - embeds.push(o.embeds[embed].toJSON()); + for (let embed in n.embeds) + embeds.push(n.embeds[embed].toJSON()); fs.appendFileSync(path, JSON.stringify({ - id: o.id, - author: o.author.id, - content: o.content, // do not use cleanContent! - time: o.createdTimestamp, + id: n.id, + author: n.author.id, + content: n.content, // do not use cleanContent! + time: n.createdTimestamp, embeds: embeds, - attachments: [...o.attachments.values()], + attachments: [...n.attachments.values()], edited: true }) + '\n'); diff --git a/src/utils/archive.js b/src/utils/archive.js index f81869e..e84e6ba 100644 --- a/src/utils/archive.js +++ b/src/utils/archive.js @@ -13,24 +13,11 @@ const fs = require('fs'); const dtf = require('@eartharoid/dtf'); const config = require('../../user/config'); -module.exports.create = (client, channel) => { +module.exports.add = (client, message) => { - // channel.members + if(message.type !== 'DEFAULT') return; - if(config.transcripts.text.enabled) { - // text/channel.txt - } - - if(config.transcripts.web.enabled) { - // raw/channel.log - } - -}; - - -module.exports.addMessage = async (client, message) => { - - if(config.transcripts.text.enabled) { // text transcripts + if (config.transcripts.text.enabled) { // text transcripts let path = `user/transcripts/text/${message.channel.id}.txt`, time = dtf('HH:mm:ss n_D MMM YY', message.createdAt), msg = message.cleanContent; @@ -39,14 +26,14 @@ module.exports.addMessage = async (client, message) => { fs.appendFileSync(path, string + '\n'); } - if(config.transcripts.web.enabled) { // web archives + if (config.transcripts.web.enabled) { // web archives let raw = `user/transcripts/raw/${message.channel.id}.log`, json = `user/transcripts/raw/entities/${message.channel.id}.json`; - + let embeds = []; for (let embed in message.embeds) embeds.push(message.embeds[embed].toJSON()); - + // message fs.appendFileSync(raw, JSON.stringify({ id: message.id, @@ -56,15 +43,22 @@ module.exports.addMessage = async (client, message) => { embeds: embeds, attachments: [...message.attachments.values()] }) + '\n'); - - // channel entities - if(!fs.existsSync(json)) - await fs.writeFileSync(json, '{}'); - - let entities = await JSON.parse(fs.readFileSync(json)); - if(!entities.users[message.author.id]) { - entities.users[message.author.id] = { + // channel entities + if (!fs.existsSync(json)) + fs.writeFileSync(json, JSON.stringify({ + channel_name: message.channel.name, + entities: { + users: {}, + channels: {}, + roles: {} + } + })); // create new + + let data = JSON.parse(fs.readFileSync(json)); + + if (!data.entities.users[message.author.id]) { + data.entities.users[message.author.id] = { avatar: message.author.avatarURL(), username: message.author.username, discriminator: message.author.discriminator, @@ -74,27 +68,44 @@ module.exports.addMessage = async (client, message) => { }; } - message.mentions.channels.each(c => entities.channels[c.id].name = c.name); + message.mentions.channels.each(c => data.entities.channels[c.id] = { + name: c.name + }); - message.mentions.roles.each(r => entities.roles[r.id] = { + message.mentions.roles.each(r => data.entities.roles[r.id] = { name: r.name, color: r.color }); - + + fs.writeFileSync(json, JSON.stringify(data)); + } }; -module.exports.export = (client, channel) => { - let path = `user/transcripts/raw/${channel.id}.log`; +module.exports.export = (client, channel) => new Promise((resolve, reject) => { - return new Promise((resolve, reject) => { - if(!config.transcripts.web.enabled || !fs.existsSync(path)) - return reject(false); + let raw = `user/transcripts/raw/${channel.id}.log`, + json = `user/transcripts/raw/entities/${channel.id}.json`; - lineReader.eachLine(path, (line, last) => { - console.log(line); - // if raw id exists, overwrite previous - // also: channel_name - }); - }); -}; \ No newline at end of file + if (!config.transcripts.web.enabled || !fs.existsSync(raw) || !fs.existsSync(json)) + return reject(false); + + let data = JSON.parse(fs.readFileSync(json)); + + data.messages = []; + + lineReader.eachLine(raw, line => { + let message = JSON.parse(line); + // data.messages[message.id] = message; + let index = data.messages.findIndex(m => m.id === message.id); + + if (index === -1) + data.messages.push(message); + else + data.messages[index] = message; + }, () => { + // fs.writeFileSync('user/data.json', JSON.stringify(data)); // FOR TESTING + // post(data).then() + resolve(config.transcripts.web.server); // json.url + }); +}); diff --git a/user/config.js b/user/config.js index 9928f9b..6d3bd97 100644 --- a/user/config.js +++ b/user/config.js @@ -51,7 +51,7 @@ module.exports = { transcripts: { text: { - enabled: false, + enabled: true, keep_for: 90, }, web: { diff --git a/user/transcripts/.json b/user/transcripts/.json deleted file mode 100644 index c87dc24..0000000 --- a/user/transcripts/.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "entities": { - "users": {}, - "channels": {}, - "roles": {} - }, - "messages": [], - "channel_name": "" - } - \ No newline at end of file diff --git a/user/transcripts/example.json b/user/transcripts/example.json deleted file mode 100644 index 2c27cd5..0000000 --- a/user/transcripts/example.json +++ /dev/null @@ -1,661 +0,0 @@ -{ - "entities": { - "users": { - "173237945149423619": { - "avatar": "https://cdn.discordapp.com/avatars/173237945149423619/a_20579a6ab02c959ce2f482ee6ec9a187.gif", - "username": "Kanin", - "discriminator": "0001", - "badge": null - }, - "337481187419226113": { - "avatar": "https://cdn.discordapp.com/avatars/337481187419226113/ae4a1951d381d6bca97cdb4ce307fbce.png", - "username": "Naila", - "discriminator": "1361", - "badge": "bot" - }, - "610443119225471007": { - "avatar": "https://cdn.discordapp.com/avatars/610443119225471007/4c9ca7e3a57c71098bf0c166d9772135.png", - "username": "Naila likes Testing", - "discriminator": "8783", - "badge": "bot" - }, - "9999": { - "avatar": "https://bowser65.xyz/avatar.b1efbb7f.png", - "username": "Bowoser the webhook", - "discriminator": "0000", - "badge": "Bot" - } - }, - "channels": { - "645102034579750922": { - "name": "python-testing" - } - }, - "roles": { - "686034948440064070": { - "name": "Test role", - "color": 10181046 - }, - "645429085656580127": { - "name": "Kanin's Bots", - "color": 7506394 - } - } - }, - "messages": [ - { - "id": "1", - "author": "9999", - "time": 1576091466245, - "content": "Did you know that webhooks can use [named links](https://myanimelist.net/anime/40010)? p cool isn't it. Spoiler: ||nobody cares :^)||" - }, - { - "id": "1", - "type": 7, - "author": "337481187419226113", - "time": 1576091466245, - "content": "Where’s <@337481187419226113>? In the server!" - }, - { - "id": "1", - "type": 8, - "author": "173237945149423619", - "time": 1576091466245, - "content": "<@173237945149423619> just boosted the server!" - }, - { - "id": "1", - "type": 10, - "author": "9999", - "time": 1576091466245, - "content": "<@9999> just boosted the server! Powercord has achieved **Level 2!**" - }, - { - "id": "1", - "author": "9999", - "time": 1576091466245, - "deleted": true, - "content": "Test message" - }, - { - "id": "1", - "author": "9999", - "time": 1576091466245, - "content": "Test message" - }, - { - "type": 10, - "id": "1", - "author": "9999", - "time": 1576091466245, - "content": "<@9999> just boosted the server! Powercord has achieved **Level 2!**" - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583650388316, - "content": "https://open.spotify.com/track/3lHSI9u83d3W5jPcvs4Mvt?si=ypkN7LIvRmi_X4gO7CSuCQ", - "embeds": [ - { - "thumbnail": { - "url": "https://i.scdn.co/image/ab67616d0000b273fd546e748b9276138041b4e1", - "proxy_url": "https://images-ext-2.discordapp.net/external/HA-QRIWHxw-U_hM4gWnvimwy-v8vkNh7ZT1c78N0nto/https/i.scdn.co/image/ab67616d0000b273fd546e748b9276138041b4e1", - "width": 640, - "height": 640 - }, - "provider": { - "name": "Spotify", - "url": null - }, - "type": "link", - "description": "Firewall, a song by Kompany on Spotify", - "url": "https://open.spotify.com/track/3lHSI9u83d3W5jPcvs4Mvt?si=ypkN7LIvRmi_X4gO7CSuCQ", - "title": "Firewall" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583650426875, - "content": "https://twitter.com/discordapp/status/1234924931328012288", - "embeds": [ - { - "footer": { - "text": "Twitter", - "icon_url": "https://abs.twimg.com/icons/apple-touch-icon-192x192.png", - "proxy_icon_url": "https://images-ext-1.discordapp.net/external/bXJWV2Y_F3XSra_kEqIYXAAsI3m1meckfLhYuWzxIfI/https/abs.twimg.com/icons/apple-touch-icon-192x192.png" - }, - "image": { - "url": "https://pbs.twimg.com/media/ESNU_9MUEAAQs9Q.jpg:large", - "proxy_url": "https://images-ext-1.discordapp.net/external/KFg4sHycrkWRsrjt95-xkAZPBdPMxRgagMmMK-BDaeo/https/pbs.twimg.com/media/ESNU_9MUEAAQs9Q.jpg%3Alarge", - "width": 2048, - "height": 1366 - }, - "id": "1", - "author": { - "name": "Discord (@discordapp)", - "url": "https://twitter.com/discordapp", - "icon_url": "https://pbs.twimg.com/profile_images/1212820842712727552/XCuWn8yF_bigger.jpg", - "proxy_icon_url": "https://images-ext-1.discordapp.net/external/1IRnHq-wu1ZgquR75Iy35WkDn2N0g1yd5wOZBjb6aHM/https/pbs.twimg.com/profile_images/1212820842712727552/XCuWn8yF_bigger.jpg" - }, - "fields": [ - { - "name": "Retweets", - "value": "93055", - "inline": true - }, - { - "name": "Likes", - "value": "60415", - "inline": true - } - ], - "color": 1942002, - "type": "rich", - "description": "We wanted to do something special for those who couldn\u2019t attend PAX East with us. \n\nRT + follow us by 3/8 for a chance to win everything pictured (yes, gaming chair IS included)", - "url": "https://twitter.com/discordapp/status/1234924931328012288" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583650448346, - "content": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", - "embeds": [ - { - "thumbnail": { - "url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg", - "proxy_url": "https://images-ext-1.discordapp.net/external/l-AFI3CsQVpcpSDYFtsDvDKag46BJ-uaQ9BTcU2JPC8/https/i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg", - "width": 1280, - "height": 720 - }, - "video": { - "url": "https://www.youtube.com/embed/dQw4w9WgXcQ", - "width": 1280, - "height": 720 - }, - "provider": { - "name": "YouTube", - "url": "https://www.youtube.com" - }, - "id": "1", - "author": { - "name": "RickAstleyVEVO", - "url": "https://www.youtube.com/channel/UC38IQsAvIsxxjztdMZQtwHA" - }, - "color": 16711680, - "type": "video", - "description": "Rick Astley's official music video for \u201cNever Gonna Give You Up\u201d \nListen to Rick Astley: https://RickAstley.lnk.to/_listenYD\n\nSubscribe to the official Rick Astley YouTube channel: https://RickAstley.lnk.to/subscribeYD\n\nFollow Rick Astley:\nFacebook: https://RickAstley.lnk.to/f...", - "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", - "title": "Rick Astley - Never Gonna Give You Up (Video)" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583650530255, - "attachments": [ - { - "id": 686029297001037844, - "filename": "handcuffs.mp4", - "size": 66494, - "height": 276, - "width": 500, - "url": "https://cdn.discordapp.com/attachments/686027141946671186/686029297001037844/handcuffs.mp4", - "proxy_url": "https://media.discordapp.net/attachments/686027141946671186/686029297001037844/handcuffs.mp4" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583650662623, - "attachments": [ - { - "id": 686029850678525970, - "filename": "Alan-Walker_Play.mp3", - "size": 4506155, - "height": null, - "width": null, - "url": "https://cdn.discordapp.com/attachments/686027141946671186/686029850678525970/Alan-Walker_Play.mp3", - "proxy_url": "https://media.discordapp.net/attachments/686027141946671186/686029850678525970/Alan-Walker_Play.mp3" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583650669549, - "content": "https://cdn.discordapp.com/attachments/686027141946671186/686029297001037844/handcuffs.mp4", - "embeds": [ - { - "video": { - "url": "https://cdn.discordapp.com/attachments/686027141946671186/686029297001037844/handcuffs.mp4", - "width": 500, - "height": 276, - "proxy_url": "https://media.discordapp.net/attachments/686027141946671186/686029297001037844/handcuffs.mp4" - }, - "type": "video", - "url": "https://cdn.discordapp.com/attachments/686027141946671186/686029297001037844/handcuffs.mp4" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583650676893, - "content": "https://cdn.discordapp.com/attachments/686027141946671186/686029850678525970/Alan-Walker_Play.mp3" - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583650695396, - "content": "n!help" - }, - { - "id": "1", - "author": "337481187419226113", - "time": 1583650695607, - "embeds": [ - { - "id": "1", - "author": { - "name": "Here is the help you requested!" - }, - "color": 11533055, - "type": "rich", - "description": "**Support server**: https://discord.gg/fox\n**Bot invite**: [Recommended perms](https://discordapp.com/oauth2/authorize?client_id=337481187419226113&scope=bot&permissions=502656087) | [No perms](https://discordapp.com/oauth2/authorize?client_id=337481187419226113&scope=bot)\n\n__**Animals**__\nbear, bird, dolphin, duck, elephant, fox, giraffe, hippo, horse, killerwhale, koala, lion, meow, panda, pig, redpanda, shark, snake, spider, turtle, woof\n\n__**BotInfo**__\napis, invite, stats\n\n__**Dev**__\nmodules\n\n__**GuildManagement**__\nguildset\n\n__**Help**__\nhelp\n\n__**Music**__\nautoplay, disconnect, now, pause, play, queue, remove, repeat, search, seek, shuffle, skip, songinfo, stop, volume\n\n__**NSFW**__\nboobbot, sheri\n\n__**Registration**__\nregister, setreg, unregister\n\n__**Reminders**__\ndeletereminder, remind, reminders\n\n__**Social**__\nbite, blush, cry, cuddle, dance, greet, highfive, hug, insult, kiss, lick, pat, poke, pout, punch, ship, shoot, shrug, slap, sleepy, smile, stare, thumbsup, tickle\n\n__**Testing**__\narchive, embed\n\n__**UserInfo**__\nuser\n\n__**Welcomer**__\nwelcomer\n\n\nType `n!help [command]` for more info on a command.\nYou can also type `n!help [category]` for more info on a category." - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583651353324, - "content": "n!eval\n```py\nfrom random import randint\nfrom datetime import datetime\nawait channel.send(embed=discord.Embed(color=random.randint(0, 0xFFFFFF), title=\"I like pizza\", description=\":D\", url=\"https://google.com/\", timestamp=datetime.now()).set_footer(text=\"This is the footer\", icon_url=\"https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fwww.purposegames.com%2Fimages%2Fgames%2Fbackground%2F355%2F355380.png&f=1&nofb=1\").set_image(url=\"https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fi.ytimg.com%2Fvi%2FxWYSzqDvTmQ%2Fhqdefault.jpg&f=1&nofb=1\").set_thumbnail(url=\"https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2Fgetwallpapers.com%2Fwallpaper%2Ffull%2F6%2F3%2F7%2F17853.jpg&f=1&nofb=1\").set_author(name=\"This is the author\", url=\"https://discordapp.com/\", icon_url=\"https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fwww.meteo.be%2Fmeteo%2Fdownload%2Ffr%2F196752%2Fimage%2Fgertgoesaert_29122006_glenelg_zuid-australi__2.jpg&f=1&nofb=1\").add_field(name=\"Field 1\", value=\"Not inline\", inline=False).add_field(name=\"Field 2\", value=\"Still not inline\", inline=False).add_field(name=\"Field 3\", value=\"Okay this one is inline\"))\n```" - }, - { - "id": "1", - "author": "337481187419226113", - "time": 1583651353470, - "embeds": [ - { - "footer": { - "text": "This is the footer", - "icon_url": "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fwww.purposegames.com%2Fimages%2Fgames%2Fbackground%2F355%2F355380.png&f=1&nofb=1", - "proxy_icon_url": "https://images-ext-2.discordapp.net/external/g1ZlkveRtSjquZ_9lsZN26CV9g-T0Cqzjr287aUTVdY/%3Fu%3Dhttps%253A%252F%252Fwww.purposegames.com%252Fimages%252Fgames%252Fbackground%252F355%252F355380.png%26f%3D1%26nofb%3D1/https/external-content.duckduckgo.com/iu/" - }, - "image": { - "url": "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fi.ytimg.com%2Fvi%2FxWYSzqDvTmQ%2Fhqdefault.jpg&f=1&nofb=1", - "proxy_url": "https://images-ext-1.discordapp.net/external/W0zZckLByXwVPGJcdi2R-yA2qCOxPnSwNTaly3kd4-s/%3Fu%3Dhttps%253A%252F%252Fi.ytimg.com%252Fvi%252FxWYSzqDvTmQ%252Fhqdefault.jpg%26f%3D1%26nofb%3D1/https/external-content.duckduckgo.com/iu/", - "width": 480, - "height": 360 - }, - "thumbnail": { - "url": "https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2Fgetwallpapers.com%2Fwallpaper%2Ffull%2F6%2F3%2F7%2F17853.jpg&f=1&nofb=1", - "proxy_url": "https://images-ext-2.discordapp.net/external/SkZBSZG4Qbg4N4YRkmX99YULtwwgArGgkZUnfqLhDiQ/%3Fu%3Dhttp%253A%252F%252Fgetwallpapers.com%252Fwallpaper%252Ffull%252F6%252F3%252F7%252F17853.jpg%26f%3D1%26nofb%3D1/https/external-content.duckduckgo.com/iu/", - "width": 2031, - "height": 1625 - }, - "id": "1", - "author": { - "name": "This is the author", - "url": "https://discordapp.com/", - "icon_url": "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fwww.meteo.be%2Fmeteo%2Fdownload%2Ffr%2F196752%2Fimage%2Fgertgoesaert_29122006_glenelg_zuid-australi__2.jpg&f=1&nofb=1", - "proxy_icon_url": "https://images-ext-2.discordapp.net/external/YcCgh239WtAHT8lvLLbv3TDYi_7y_nug2RoVeL36fQU/%3Fu%3Dhttps%253A%252F%252Fwww.meteo.be%252Fmeteo%252Fdownload%252Ffr%252F196752%252Fimage%252Fgertgoesaert_29122006_glenelg_zuid-australi__2.jpg%26f%3D1%26nofb%3D1/https/external-content.duckduckgo.com/iu/" - }, - "fields": [ - { - "name": "Field 1", - "value": "Not inline", - "inline": false - }, - { - "name": "Field 2", - "value": "Still not inline", - "inline": false - }, - { - "name": "Field 3", - "value": "Okay this one is inline", - "inline": true - } - ], - "color": 11492902, - "timestamp": "2020-03-07T21:09:13.380000+00:00", - "type": "rich", - "description": ":D", - "url": "https://google.com/", - "title": "I like pizza" - } - ] - }, - { - "id": "1", - "author": "337481187419226113", - "time": 1583651353649, - "content": "```py\n>>> from random import randint\n... from datetime import datetime\n... await channel.send(embed=discord.Embed(color=random.randint(0, 0xFFFFFF), title=\"I like pizza\", description=\":D\", url=\"https://google.com/\", timestamp=datetime.now()).set_footer(text=\"This is the footer\", icon_url=\"https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fwww.purposegames.com%2Fimages%2Fgames%2Fbackground%2F355%2F355380.png&f=1&nofb=1\").set_image(url=\"https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fi.ytimg.com%2Fvi%2FxWYSzqDvTmQ%2Fhqdefault.jpg&f=1&nofb=1\").set_thumbnail(url=\"https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2Fgetwallpapers.com%2Fwallpaper%2Ffull%2F6%2F3%2F7%2F17853.jpg&f=1&nofb=1\").set_author(name=\"This is the author\", url=\"https://discordapp.com/\", icon_url=\"https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fwww.meteo.be%2Fmeteo%2Fdownload%2Ffr%2F196752%2Fimage%2Fgertgoesaert_29122006_glenelg_zuid-australi__2.jpg&f=1&nofb=1\").add_field(name=\"Field 1\", value=\"Not inline\", inline=False).add_field(name=\"Field 2\", value=\"Still not inline\", inline=False).add_field(name=\"Field 3\", value=\"Okay this one is inline\"))\n... \n\n\nTime to execute: 177.601ms```" - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583651467343, - "attachments": [ - { - "id": 686033227827249157, - "filename": "xelA_settings.json", - "size": 3763, - "height": null, - "width": null, - "url": "https://cdn.discordapp.com/attachments/686027141946671186/686033227827249157/xelA_settings.json", - "proxy_url": "https://media.discordapp.net/attachments/686027141946671186/686033227827249157/xelA_settings.json" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583651524708, - "attachments": [ - { - "id": 686033467573927966, - "filename": "Naila.png", - "size": 2411472, - "height": 4464, - "width": 2640, - "url": "https://cdn.discordapp.com/attachments/686027141946671186/686033467573927966/Naila.png", - "proxy_url": "https://media.discordapp.net/attachments/686027141946671186/686033467573927966/Naila.png" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583651531191, - "content": "https://cdn.discordapp.com/attachments/686027141946671186/686033467573927966/Naila.png", - "embeds": [ - { - "thumbnail": { - "url": "https://cdn.discordapp.com/attachments/686027141946671186/686033467573927966/Naila.png", - "proxy_url": "https://media.discordapp.net/attachments/686027141946671186/686033467573927966/Naila.png", - "width": 2640, - "height": 4464 - }, - "type": "image", - "url": "https://cdn.discordapp.com/attachments/686027141946671186/686033467573927966/Naila.png" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583651570256, - "attachments": [ - { - "id": 686033658985185308, - "filename": "iu.png", - "size": 167870, - "height": 248, - "width": 480, - "url": "https://cdn.discordapp.com/attachments/686027141946671186/686033658985185308/iu.png", - "proxy_url": "https://media.discordapp.net/attachments/686027141946671186/686033658985185308/iu.png" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583651611768, - "content": "https://tenor.com/view/illya-blink-anime-eyes-cute-gif-16059710", - "embeds": [ - { - "thumbnail": { - "url": "https://media.tenor.co/images/8b1afd9e853b9c5a7af6fbb0f8b661ce/tenor.png", - "proxy_url": "https://images-ext-2.discordapp.net/external/jXg7OeyDcV_YDkrCpXNqNSpfOtWhY3UfXWzQiWZZrsA/https/media.tenor.co/images/8b1afd9e853b9c5a7af6fbb0f8b661ce/tenor.png", - "width": 640, - "height": 360 - }, - "video": { - "url": "https://media.tenor.co/videos/dfb2eb33b95df0c579bc966cb664aaec/mp4", - "width": 640, - "height": 360 - }, - "provider": { - "name": "Tenor", - "url": "https://tenor.co" - }, - "type": "gifv", - "url": "https://tenor.com/view/illya-blink-anime-eyes-cute-gif-16059710" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583651646667, - "content": "<:denArcticFox:637121071962783754>\ud83d\ude02\u2764\ufe0f<:denDafuck:638581806471446569><:eyessquint:521498616783962123>\ud83c\udde89\ufe0f\u20e3<:Pog:459080170100228096>" - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583651766661, - "content": "<:awoo1:331095654678003722><:awoo2:331095654439059468><:awoo3:331095654611025921><:awoo4:331095655130857482><:awoo5:331095655068073985>\n<:awoo6:331095655080656899><:awoo7:331095655407812608><:awoo8:331095657270214656><:awoo9:331095657102180354><:awoo10:331095656607383552>\n<:awoo11:331095656368439296><:awoo12:331095657639051265><:awoo13:331095657853222912><:awoo14:331095657714548736><:awoo15:331095656297136130>\n<:awoo16:331095656422965248><:awoo17:331095657232465920><:awoo18:331095656921956362><:awoo19:331095657882320896><:awoo20:331095657500770305>\n<:awoo21:331095656854847489><:awoo22:331095657655959552><:awoo23:331095657311895553><:awoo24:331095657181872129><:awoo25:331095657102180352>" - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583651772258, - "content": "Those are emojis btw" - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583651852254, - "content": "discord. gg/fox discord. gg/discord-feedback" - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583651903319, - "content": "<@&686034948440064070> <@!173237945149423619> <@&645429085656580127> <#645102034579750922>" - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583673485361, - "content": "> This is a quote" - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583673497315, - "content": "> lots\n> of\n> quotes" - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583673521320, - "content": "n!!archive 100" - }, - { - "id": "1", - "author": "610443119225471007", - "time": 1583673523550, - "attachments": [ - { - "id": 686140837285199892, - "filename": "data.json", - "size": 22318, - "height": null, - "width": null, - "url": "https://cdn.discordapp.com/attachments/686027141946671186/686140837285199892/data.json", - "proxy_url": "https://media.discordapp.net/attachments/686027141946671186/686140837285199892/data.json" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583673585521, - "content": "too much tabbing" - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583673588834, - "content": "n!!archive 100" - }, - { - "id": "1", - "author": "610443119225471007", - "time": 1583673590802, - "attachments": [ - { - "id": 686141119293292584, - "filename": "data.json", - "size": 20074, - "height": null, - "width": null, - "url": "https://cdn.discordapp.com/attachments/686027141946671186/686141119293292584/data.json", - "proxy_url": "https://media.discordapp.net/attachments/686027141946671186/686141119293292584/data.json" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583674090609, - "content": "So since the last one loaded as a png have an uploaded gif", - "attachments": [ - { - "id": 686143215606431808, - "filename": "dance.gif", - "size": 1657487, - "height": 270, - "width": 480, - "url": "https://cdn.discordapp.com/attachments/686027141946671186/686143215606431808/dance.gif", - "proxy_url": "https://media.discordapp.net/attachments/686027141946671186/686143215606431808/dance.gif" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583674098301, - "content": "n!!archive 100" - }, - { - "id": "1", - "author": "610443119225471007", - "time": 1583674099651, - "attachments": [ - { - "id": 686143254000959530, - "filename": "data.json", - "size": 21165, - "height": null, - "width": null, - "url": "https://cdn.discordapp.com/attachments/686027141946671186/686143254000959530/data.json", - "proxy_url": "https://media.discordapp.net/attachments/686027141946671186/686143254000959530/data.json" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583674619343, - "content": "https://twitter.com/SpaceX/status/1236172031919448067?s=20", - "embeds": [ - { - "footer": { - "text": "Twitter", - "icon_url": "https://abs.twimg.com/icons/apple-touch-icon-192x192.png", - "proxy_icon_url": "https://images-ext-1.discordapp.net/external/bXJWV2Y_F3XSra_kEqIYXAAsI3m1meckfLhYuWzxIfI/https/abs.twimg.com/icons/apple-touch-icon-192x192.png" - }, - "image": { - "url": "https://pbs.twimg.com/media/ESfDyyvUMAALqKA.jpg:large", - "proxy_url": "https://images-ext-2.discordapp.net/external/Sr4lbVDZzceMRo1E9f6-q5hpypkr7ksiOyUivMAJ6_4/https/pbs.twimg.com/media/ESfDyyvUMAALqKA.jpg%3Alarge", - "width": 2048, - "height": 1365 - }, - "id": "1", - "author": { - "name": "SpaceX (@SpaceX)", - "url": "https://twitter.com/SpaceX", - "icon_url": "https://pbs.twimg.com/profile_images/1082744382585856001/rH_k3PtQ_bigger.jpg", - "proxy_icon_url": "https://images-ext-2.discordapp.net/external/YWj4_0iHgQjUu-YFui4-4oQj5PKkm5D5zuSYlajgnNg/https/pbs.twimg.com/profile_images/1082744382585856001/rH_k3PtQ_bigger.jpg" - }, - "fields": [ - { - "name": "Retweets", - "value": "2582", - "inline": true - }, - { - "name": "Likes", - "value": "17395", - "inline": true - } - ], - "color": 1942002, - "type": "rich", - "description": "Falcon 9 launches the final mission of the first version of Dragon", - "url": "https://twitter.com/SpaceX/status/1236172031919448067?s=20" - }, - { - "image": { - "url": "https://pbs.twimg.com/media/ESfDyywUcAASwpl.jpg:large", - "proxy_url": "https://images-ext-2.discordapp.net/external/Eev3nZHn0gFQmoV_BcoIaCTokqoOXSPIDjT-e4Tt7pY/https/pbs.twimg.com/media/ESfDyywUcAASwpl.jpg%3Alarge", - "width": 2048, - "height": 1365 - }, - "type": "rich", - "url": "https://twitter.com/SpaceX/status/1236172031919448067?s=20" - }, - { - "image": { - "url": "https://pbs.twimg.com/media/ESfD0T4VAAEpRIe.jpg:large", - "proxy_url": "https://images-ext-1.discordapp.net/external/N9fvJXQA0DeBBITbLyHr3TiTUrP3F7UpQiA6Xey13sE/https/pbs.twimg.com/media/ESfD0T4VAAEpRIe.jpg%3Alarge", - "width": 2048, - "height": 1365 - }, - "type": "rich", - "url": "https://twitter.com/SpaceX/status/1236172031919448067?s=20" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583674629651, - "content": "n!!archive 100" - }, - { - "id": "1", - "author": "610443119225471007", - "time": 1583674630978, - "attachments": [ - { - "id": 686145482271358984, - "filename": "data.json", - "size": 24442, - "height": null, - "width": null, - "url": "https://cdn.discordapp.com/attachments/686027141946671186/686145482271358984/data.json", - "proxy_url": "https://media.discordapp.net/attachments/686027141946671186/686145482271358984/data.json" - } - ] - }, - { - "id": "1", - "author": "173237945149423619", - "time": 1583687671807, - "content": "n!!archive 100" - } - ], - "channel_name": "test-archive" - } - \ No newline at end of file