|
// ============================================================================ // |
|
|
|
/* |
|
* Terrence v1.0.0 |
|
* Written by SamJakob. |
|
* |
|
* Changes the guild icons of any guilds the bot is a member of every N seconds |
|
* as defined by [config.updateImageTimeIntervalSeconds] below. |
|
*/ |
|
|
|
// ============================================================================ // |
|
|
|
// Add permissions if necessary from: |
|
// https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags |
|
const PERMISSION_ADMINISTRATOR = 0x00000008; |
|
const PERMISSION_MANAGE_GUILD = 0x00000020; |
|
const PERMISSION_MANAGE_MESSAGES = 0x00002000; |
|
|
|
// If this wasn't designed to be copy-pasted, I'd use a .env file for this: |
|
const secrets = { |
|
DISCORD_API_CLIENT_TOKEN: "", |
|
IMGUR_API_CLIENT_ID: "", |
|
|
|
// Do not include a trailing slash. |
|
IMGUR_ALBUM_URL: "" |
|
}; |
|
|
|
const config = { |
|
// The time interval at which the Guild icon should be updated. |
|
// (default: 3600 = 1 hour) |
|
updateImageTimeIntervalSeconds: 3600, |
|
|
|
// Whether or not the bot should show a 'no permission' message if an |
|
// unauthorized user attempts to run a command. |
|
showNoPermissionMessage: true, |
|
|
|
// The prefix that must be prepended to any commands a user wishes to |
|
// run. When checking commands, this is simply concatenated directly |
|
// with the command name, so you must include any symbols you may wish |
|
// to have seperating the command prefix from the command itself. |
|
// e.g. setting this to 't.' would cause 'myCommand' to be t.myCommand |
|
commandPrefix: 't.', |
|
|
|
// Requires that any users wishing to execute commands have the below |
|
// permission. |
|
// Set to 0 to disable this check, and to require multiple permissions, |
|
// simply bitwise XOR the permissions. |
|
// e.g. for both Administrator and Manage Guild permissions, one would use: |
|
// commandRequiresPermission: PERMISSION_ADMINISTRATOR | PERMISSION_MANAGE_GUILD |
|
commandRequiresPermission: PERMISSION_ADMINISTRATOR, |
|
}; |
|
|
|
// ============================================================================ // |
|
|
|
// Method to override the Array object's prototype to include a .last method. |
|
// |
|
// Doing something like this isn't highly recommended, but it's quick and pretty |
|
// clean and ought to work without issue for something like this. |
|
if (!Array.prototype.last) { |
|
Array.prototype.last = function() { |
|
return this[this.length - 1]; |
|
}; |
|
}; |
|
|
|
// Method to override the Array object's prototype to include a .random method. |
|
// |
|
// See .last's comment for the JavaScript community's view on doing s--- like this. |
|
if (!Array.prototype.random) { |
|
Array.prototype.random = function() { |
|
return this[Math.floor(Math.random() * this.length)]; |
|
} |
|
} |
|
|
|
// ============================================================================ // |
|
|
|
// Naïve approach to extract the album hash from the album URL. |
|
const ALBUM_HASH = secrets.IMGUR_ALBUM_URL.split("/").last(); |
|
|
|
/** |
|
* Returns the link that can be used to invite the bot to a Discord server with the |
|
* specified permission level. |
|
* |
|
* @param {string} clientId The Discord bot's client ID. |
|
* @param {number} permissionLevel The desired permission level of the bot when invited to the server. |
|
*/ |
|
function generateInviteUrl(clientId, permissionLevel) { |
|
return `https://discord.com/oauth2/authorize?client_id=${clientId}&scope=bot&permissions=${permissionLevel}`; |
|
} |
|
|
|
/** |
|
* Fetches the latest list of images from the Imgur API. |
|
* |
|
* @param {string} albumHash The hash of the imgur album to acquire the list of images from. |
|
* @returns {Promise<string[]|null>} Either an array of direct links to the images in the album or null if the request failed. |
|
*/ |
|
async function acquireImageList(albumHash) { |
|
|
|
console.log("Acquiring image list..."); |
|
|
|
try { |
|
|
|
// Fetch the list of items in the album from Imgur's API. |
|
const imgurData = (await axios.get(`https://api.imgur.com/3/album/${ALBUM_HASH}/images`, { |
|
headers: { |
|
Authorization: `Client-ID ${secrets.IMGUR_API_CLIENT_ID}` |
|
} |
|
})).data; |
|
|
|
// and map them such that they are only the direct link to each image. |
|
return imgurData.data.map((item) => { |
|
return item.link; |
|
}); |
|
|
|
} catch(ex) { |
|
console.error("Failed to acquire image list."); |
|
console.error(ex); |
|
return null; |
|
} |
|
|
|
} |
|
|
|
/** |
|
* Updates the image of ALL the guilds the bot is a member of to be the specified imageUrl. |
|
* |
|
* @param {Discord.Client} client The Discord client that is a member of the guilds that should be updated. |
|
* @param {string} imageUrl The URL of the image that should be made the icon of each guild. |
|
*/ |
|
async function applyImage(client, imageUrl) { |
|
|
|
for (let cachedGuild of client.guilds.cache) { |
|
cachedCurrentImage = imageUrl; |
|
|
|
let guild = await client.guilds.resolve(cachedGuild[0]); |
|
await guild.setIcon(imageUrl); |
|
} |
|
|
|
} |
|
|
|
/** |
|
* Randomly selects a new image, **OTHER** than the one that is currently in place |
|
* and applies it as the new guild icon for ALL the guilds the bot is a member of. |
|
* |
|
* @param {string} client The Discord client that is a member of the guilds that should be updated. |
|
*/ |
|
async function applyRandomNewImage(client) { |
|
// Ensure that the new randomly selected image is not the same as the old. |
|
let newImage = cachedCurrentImage; |
|
do { |
|
newImage = imageList.random(); |
|
} while (newImage == cachedCurrentImage); |
|
|
|
// Then, apply the new image. |
|
await applyImage(client, newImage); |
|
} |
|
|
|
/** |
|
* If config.showNoPermissionMessage is enabled, it will send a response to the |
|
* provided message indicating that the user has no permission to perform a given |
|
* operation. |
|
* |
|
* Otherwise, it will do nothing. |
|
* |
|
* @param {string} message The message to respond to with the no permission message. |
|
*/ |
|
async function sendNoPermissionResponse(message) { |
|
if (config.showNoPermissionMessage) { |
|
let hasManageMessagePermission = message.guild.member(client.user).permissions.has(PERMISSION_MANAGE_MESSAGES); |
|
const manageMessageNotice = hasManageMessagePermission ? "\n_(This will be deleted after 10 seconds)_" : ""; |
|
|
|
let response = await message.reply(`❌ You do not have permission to run \`${message.content}\`!${manageMessageNotice}`); |
|
|
|
if (hasManageMessagePermission) setTimeout(async () => { |
|
await response.delete(); |
|
}, 10000); |
|
} |
|
} |
|
|
|
// ============================================================================ // |
|
|
|
const axios = require('axios'); |
|
const Discord = require('discord.js'); |
|
const client = new Discord.Client(); |
|
|
|
// The list of images from which to select a guild icon. |
|
let cachedCurrentImage; |
|
let imageList; |
|
|
|
client.on('message', async (message) => { |
|
|
|
// If the bot has permission to manage messages, it will delete command-related messages after use |
|
// to keep the chat logs clean. |
|
let hasManageMessagePermission = message.guild.member(client.user).permissions.has(PERMISSION_MANAGE_MESSAGES); |
|
const manageMessageNotice = hasManageMessagePermission ? "\n_(This message will be deleted after 10 seconds.)_" : ""; |
|
|
|
/** |
|
* Command: update |
|
* |
|
* Allows a user to indicate that an update to the Imgur album is available for the bot. |
|
* Thus, allowing the cached image list from the album to be updated without restarting the bot. |
|
*/ |
|
if (message.content == `${config.commandPrefix}update`) { |
|
|
|
if (message.guild.member(message.author).permissions.has(config.commandRequiresPermission)) { |
|
// Indicate that the command was understood, and if the bot has permission to manage messages |
|
// remove the message with the initial command. |
|
let reply = await message.reply("Updating image list, please wait..."); |
|
if (hasManageMessagePermission) await message.delete(); |
|
|
|
// Acquire the new image list and reply with the status of the operation. |
|
if (imageList = await acquireImageList(ALBUM_HASH)) { |
|
await reply.edit(`The image list has been updated successfully.${manageMessageNotice}`); |
|
} else { |
|
await reply.edit(`An error occurred whilst updating the image list.${manageMessageNotice}`); |
|
} |
|
|
|
// Finally, delete the status response. |
|
if (hasManageMessagePermission) setTimeout(async () => { |
|
await reply.delete(); |
|
}, 10000); |
|
} else sendNoPermissionResponse(message); |
|
|
|
} |
|
|
|
/** |
|
* Command: change |
|
* |
|
* Allows a user to pre-emptively change the Guild icon to another random image from |
|
* the image list. |
|
*/ |
|
if (message.content == `${config.commandPrefix}change`) { |
|
|
|
if (message.guild.member(message.author).permissions.has(config.commandRequiresPermission)) { |
|
// Indicate that the command was understood, and if the bot has permission to manage messages |
|
// remove the message with the initial command. |
|
let reply = await message.reply("Selecting a new image, please wait..."); |
|
if (hasManageMessagePermission) await message.delete(); |
|
|
|
try { |
|
|
|
// Randomly apply a new image but make sure it is not the same |
|
// image as the current. |
|
applyRandomNewImage(client); |
|
|
|
await reply.edit(`The server icon has been updated successfully.${manageMessageNotice}`); |
|
} catch (ex) { |
|
await reply.edit(`Failed to update the server icon.${manageMessageNotice}`); |
|
} |
|
|
|
// Finally, delete the status response. |
|
if (hasManageMessagePermission) setTimeout(async () => { |
|
await reply.delete(); |
|
}, 10000); |
|
} else sendNoPermissionResponse(message); |
|
|
|
} |
|
|
|
}); |
|
|
|
(async () => { |
|
|
|
// Attempt to acquire the initial list of guild icons, or - on failure - simply exit. |
|
if (!(imageList = await acquireImageList(ALBUM_HASH))) { |
|
process.exit(1); |
|
} |
|
|
|
console.log(`Found ${imageList.length} images.`); |
|
console.log(""); |
|
|
|
// Log in to Discord's API. |
|
console.log("Logging in..."); |
|
await client.login(secrets.DISCORD_API_CLIENT_TOKEN); |
|
|
|
console.log(""); |
|
console.log("==============================================="); |
|
console.log(`Client User:\t\t${client.user.tag}`); |
|
console.log(`Invite URL:\t\t${ generateInviteUrl(client.user.id, PERMISSION_MANAGE_GUILD | PERMISSION_MANAGE_MESSAGES) }`); |
|
console.log("==============================================="); |
|
console.log(""); |
|
console.log("The 'Manage Guild' permission is mandatory, however 'Manage Messages' is optional and is simply used to delete command-related messages."); |
|
|
|
console.log(""); |
|
console.log("Applying an initial image..."); |
|
await applyImage(client, imageList.random()); |
|
console.log("Applied initial image."); |
|
|
|
console.log(""); |
|
console.log("Setting up interval..."); |
|
|
|
setInterval(() => { |
|
// Randomly apply a new image but make sure it is not the same |
|
// image as the current. |
|
applyRandomNewImage(client); |
|
}, config.updateImageTimeIntervalSeconds * 1000); |
|
|
|
console.log(`The guilds' icon will be updated every ${config.updateImageTimeIntervalSeconds} seconds starting from ${new Date()}.`); |
|
|
|
})(); |
how do download gist?