dev sync latest
13
.env.example
@ -1,14 +1,21 @@
|
|||||||
|
@@ -1,23 +0,0 @@
|
||||||
BOT_TOKEN=
|
BOT_TOKEN=
|
||||||
|
|
||||||
MONGO_CONNECTION=mongodb://0.0.0.0:27017
|
MONGO_CONNECTION=
|
||||||
|
|
||||||
|
# Webhooks [Optional]
|
||||||
ERROR_LOGS=
|
ERROR_LOGS=
|
||||||
JOIN_LEAVE_LOGS=
|
JOIN_LEAVE_LOGS=
|
||||||
|
|
||||||
BOT_SECRET=HtLZgRhB3BCsAurCnaew262TylrJV5-Q
|
BOT_SECRET=
|
||||||
SESSION_PASSWORD=AmaneChanDesu12
|
SESSION_PASSWORD=
|
||||||
|
|
||||||
|
# Required for Weather Command (https://weatherstack.com)
|
||||||
WEATHERSTACK_KEY=
|
WEATHERSTACK_KEY=
|
||||||
|
|
||||||
|
# Required for image commands (https://strangeapi.fun/docs)
|
||||||
|
STRANGE_API_KEY=
|
||||||
|
|
||||||
|
# SPOTFIY [Required for Spotify Support]
|
||||||
SPOTIFY_CLIENT_ID=
|
SPOTIFY_CLIENT_ID=
|
||||||
SPOTIFY_CLIENT_SECRET=
|
SPOTIFY_CLIENT_SECRET=
|
17
.eslintrc.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"commonjs": true,
|
||||||
|
"es2021": true
|
||||||
|
},
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 12
|
||||||
|
},
|
||||||
|
"plugins": ["jsdoc"],
|
||||||
|
"rules": {
|
||||||
|
"no-unused-vars": ["error", { "args": "none" }],
|
||||||
|
"jsdoc/no-undefined-types": 1,
|
||||||
|
"no-cond-assign": 0
|
||||||
|
}
|
||||||
|
}
|
4
.gitbook.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
root: ./docs/
|
||||||
|
structure:
|
||||||
|
readme: ../README.md
|
||||||
|
summary: SUMMARY.md
|
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
|||||||
node_modules
|
node_modules
|
||||||
docs
|
docs
|
||||||
|
.env.asli
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
|
11
.prettierrc.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"printWidth": 120,
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"arrowParens": "always",
|
||||||
|
"endOfLine": "crlf"
|
||||||
|
}
|
28
bot.js
@ -1,11 +1,17 @@
|
|||||||
require("dotenv").config();
|
require("dotenv").config();
|
||||||
require("module-alias/register");
|
require("module-alias/register");
|
||||||
|
|
||||||
const path = require("path");
|
// register extenders
|
||||||
const { startupCheck } = require("@utils/botUtils");
|
require("@helpers/extenders/Message");
|
||||||
const { BotClient } = require("@src/structures");
|
require("@helpers/extenders/Guild");
|
||||||
|
require("@helpers/extenders/GuildChannel");
|
||||||
|
|
||||||
global.__appRoot = path.resolve(__dirname);
|
const { checkForUpdates } = require("@helpers/BotUtils");
|
||||||
|
const { initializeMongoose } = require("@src/database/mongoose");
|
||||||
|
const { BotClient } = require("@src/structures");
|
||||||
|
const { validateConfiguration } = require("@helpers/Validator");
|
||||||
|
|
||||||
|
validateConfiguration();
|
||||||
|
|
||||||
// initialize client
|
// initialize client
|
||||||
const client = new BotClient();
|
const client = new BotClient();
|
||||||
@ -13,21 +19,29 @@ client.loadCommands("src/commands");
|
|||||||
client.loadContexts("src/contexts");
|
client.loadContexts("src/contexts");
|
||||||
client.loadEvents("src/events");
|
client.loadEvents("src/events");
|
||||||
|
|
||||||
|
|
||||||
// find unhandled promise rejections
|
// find unhandled promise rejections
|
||||||
process.on("unhandledRejection", (err) => client.logger.error(`Unhandled exception`, err));
|
process.on("unhandledRejection", (err) => client.logger.error(`Unhandled exception`, err));
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
await startupCheck();
|
// check for updates
|
||||||
|
await checkForUpdates();
|
||||||
|
|
||||||
|
// start the dashboard
|
||||||
if (client.config.DASHBOARD.enabled) {
|
if (client.config.DASHBOARD.enabled) {
|
||||||
client.logger.log("Launching dashboard");
|
client.logger.log("Launching dashboard");
|
||||||
try {
|
try {
|
||||||
const { launch } = require("@root/dashboard/app");
|
const { launch } = require("@root/dashboard/app");
|
||||||
|
|
||||||
|
// let the dashboard initialize the database
|
||||||
await launch(client);
|
await launch(client);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
client.logger.error("Failed to launch dashboard", ex);
|
client.logger.error("Failed to launch dashboard", ex);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// initialize the database
|
||||||
|
await initializeMongoose();
|
||||||
}
|
}
|
||||||
await client.initializeMongoose();
|
|
||||||
|
// start the client
|
||||||
await client.login(process.env.BOT_TOKEN);
|
await client.login(process.env.BOT_TOKEN);
|
||||||
})();
|
})();
|
||||||
|
194
config.js
@ -1,104 +1,23 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
OWNER_IDS: ["334307216926703616","1010905208391487548"], // Bot owner ID's
|
OWNER_IDS: ["334307216926703616","1010905208391487548"], // Bot owner ID's
|
||||||
PREFIX: "::", // Default prefix for the bot
|
|
||||||
SUPPORT_SERVER: "https://kiera-bot.serenetia.com", // Your bot support server
|
SUPPORT_SERVER: "https://kiera-bot.serenetia.com", // Your bot support server
|
||||||
PRESENCE: {
|
PREFIX_COMMANDS: {
|
||||||
ENABLED: true, // Whether or not the bot should update its status
|
ENABLED: true, // Enable/Disable prefix commands
|
||||||
STATUS: "online", // The bot's status [online, idle, dnd, invisible]
|
DEFAULT_PREFIX: "kt?", // Default prefix for the bot
|
||||||
TYPE: "LISTENING", // Status type for the bot [PLAYING | LISTENING | WATCHING | COMPETING]
|
|
||||||
MESSAGE: "/play with {members} members in {servers} servers", // Your bot status message
|
|
||||||
},
|
|
||||||
DASHBOARD: {
|
|
||||||
enabled: true, // enable or disable dashboard
|
|
||||||
baseURL: "https://dash-kiera.serenetia.com", // base url
|
|
||||||
failureURL: "https://dash-kiera.serenetia.com", // failure redirect url
|
|
||||||
port: "3559", // port to run the bot on
|
|
||||||
},
|
},
|
||||||
INTERACTIONS: {
|
INTERACTIONS: {
|
||||||
SLASH: true, // Should the interactions be enabled
|
SLASH: true, // Should the interactions be enabled
|
||||||
CONTEXT: true, // Should contexts be enabled
|
CONTEXT: true, // Should contexts be enabled
|
||||||
GLOBAL: true, // Should the interactions be registered globally
|
GLOBAL: true, // Should the interactions be registered globally
|
||||||
TEST_GUILD_ID: "753500548656791573", // Guild ID where the interactions should be registered. [** Test you commands here first **] //NOT REQUIRED
|
TEST_GUILD_ID: "753500548656791573", // Guild ID where the interactions should be registered. [** Test you commands here first **]
|
||||||
},
|
},
|
||||||
XP_SYSTEM: {
|
|
||||||
COOLDOWN: 5, // Cooldown in seconds between messages
|
|
||||||
DEFAULT_LVL_UP_MSG: "{m}, You just advanced to **Level {l}**",
|
|
||||||
},
|
|
||||||
MISCELLANEOUS: {
|
|
||||||
DAILY_COINS: 100, // coins to be received by daily command
|
|
||||||
},
|
|
||||||
ECONOMY: {
|
|
||||||
CURRENCY: "₪",
|
|
||||||
DAILY_COINS: 1000, // coins to be received by daily command
|
|
||||||
MIN_BEG_AMOUNT: 1000, // minimum coins to be received when beg command is used
|
|
||||||
MAX_BEG_AMOUNT: 25000, // maximum coins to be received when beg command is used
|
|
||||||
},
|
|
||||||
SUGGESTIONS: {
|
|
||||||
ENABLED: true, // Should the suggestion system be enabled
|
|
||||||
EMOJI: {
|
|
||||||
UP_VOTE: "⬆️",
|
|
||||||
DOWN_VOTE: "⬇️",
|
|
||||||
},
|
|
||||||
DEFAULT_EMBED: "#0099ff",
|
|
||||||
APPROVED_EMBED: "#00ff00",
|
|
||||||
DENIED_EMBED: "#ff0000",
|
|
||||||
},
|
|
||||||
IMAGE: {
|
|
||||||
BASE_API: "https://image-api.strangebot.xyz",
|
|
||||||
},
|
|
||||||
MUSIC: {
|
|
||||||
IDLE_TIME: 60, // Time in seconds before the bot disconnects from the voice channel
|
|
||||||
MAX_SEARCH_RESULTS: 20,
|
|
||||||
NODES: [
|
|
||||||
{
|
|
||||||
host: "lavalink.serenetia.com",
|
|
||||||
port: 443,
|
|
||||||
password: "amanechan",
|
|
||||||
secure: true,
|
|
||||||
version: "v3",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
host: "lavalink-sg.serenetia.com",
|
|
||||||
port: 443,
|
|
||||||
password: "amanechan",
|
|
||||||
secure: true,
|
|
||||||
version: "v3",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
host: "lavalink-sg2.serenetia.com",
|
|
||||||
port: 443,
|
|
||||||
password: "amanechan",
|
|
||||||
secure: true,
|
|
||||||
version: "v4",
|
|
||||||
useVersionPath: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
/* Bot Embed Colors */
|
|
||||||
EMBED_COLORS: {
|
EMBED_COLORS: {
|
||||||
BOT_EMBED: "#068ADD",
|
BOT_EMBED: "#068ADD",
|
||||||
TRANSPARENT: "#36393F",
|
TRANSPARENT: "#36393F",
|
||||||
SUCCESS: "#00A56A",
|
SUCCESS: "#00A56A",
|
||||||
ERROR: "#D61A3C",
|
ERROR: "#D61A3C",
|
||||||
WARNING: "#F7E919",
|
WARNING: "#F7E919",
|
||||||
AUTOMOD: "#36393F",
|
|
||||||
TICKET_CREATE: "#068ADD",
|
|
||||||
TICKET_CLOSE: "#068ADD",
|
|
||||||
TIMEOUT_LOG: "#102027",
|
|
||||||
UNTIMEOUT_LOG: "#4B636E",
|
|
||||||
KICK_LOG: "#FF7961",
|
|
||||||
SOFTBAN_LOG: "#AF4448",
|
|
||||||
BAN_LOG: "#D32F2F",
|
|
||||||
VMUTE_LOG: "#102027",
|
|
||||||
VUNMUTE_LOG: "#4B636E",
|
|
||||||
DEAFEN_LOG: "#102027",
|
|
||||||
UNDEAFEN_LOG: "#4B636E",
|
|
||||||
DISCONNECT_LOG: "RANDOM",
|
|
||||||
MOVE_LOG: "RANDOM",
|
|
||||||
GIVEAWAYS: "#FF468A",
|
|
||||||
UPDATE: "#000000",
|
|
||||||
},
|
},
|
||||||
/* Maximum number of keys that can be stored */
|
|
||||||
CACHE_SIZE: {
|
CACHE_SIZE: {
|
||||||
GUILDS: 10000,
|
GUILDS: 10000,
|
||||||
USERS: 1000000,
|
USERS: 1000000,
|
||||||
@ -107,4 +26,109 @@ module.exports = {
|
|||||||
MESSAGES: {
|
MESSAGES: {
|
||||||
API_ERROR: "Unexpected Backend Error! Try again later or contact support server",
|
API_ERROR: "Unexpected Backend Error! Try again later or contact support server",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// PLUGINS
|
||||||
|
|
||||||
|
AUTOMOD: {
|
||||||
|
ENABLED: false,
|
||||||
|
LOG_EMBED: "#36393F",
|
||||||
|
DM_EMBED: "#36393F",
|
||||||
|
},
|
||||||
|
|
||||||
|
DASHBOARD: {
|
||||||
|
enabled: true, // enable or disable dashboard
|
||||||
|
baseURL: "http://localhost:8080", // base url
|
||||||
|
failureURL: "http://localhost:8080", // failure redirect url
|
||||||
|
port: "8080", // port to run the bot on
|
||||||
|
},
|
||||||
|
|
||||||
|
ECONOMY: {
|
||||||
|
ENABLED: false,
|
||||||
|
CURRENCY: "₪",
|
||||||
|
DAILY_COINS: 100, // coins to be received by daily command
|
||||||
|
MIN_BEG_AMOUNT: 100, // minimum coins to be received when beg command is used
|
||||||
|
MAX_BEG_AMOUNT: 2500, // maximum coins to be received when beg command is used
|
||||||
|
},
|
||||||
|
|
||||||
|
MUSIC: {
|
||||||
|
ENABLED: true,
|
||||||
|
IDLE_TIME: 60, // Time in seconds before the bot disconnects from an idle voice channel
|
||||||
|
MAX_SEARCH_RESULTS: 5,
|
||||||
|
DEFAULT_SOURCE: "YT", // YT = Youtube, YTM = Youtube Music, SC = SoundCloud
|
||||||
|
// Add any number of lavalink nodes here
|
||||||
|
// Refer to https://github.com/freyacodes/Lavalink to host your own lavalink server
|
||||||
|
LAVALINK_NODES: [
|
||||||
|
{
|
||||||
|
host: "103.125.38.143",
|
||||||
|
port: 3556,
|
||||||
|
password: "amanechan",
|
||||||
|
id: "Indonesia Node",
|
||||||
|
secure: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
GIVEAWAYS: {
|
||||||
|
ENABLED: false,
|
||||||
|
REACTION: "🎁",
|
||||||
|
START_EMBED: "#FF468A",
|
||||||
|
END_EMBED: "#FF468A",
|
||||||
|
},
|
||||||
|
|
||||||
|
IMAGE: {
|
||||||
|
ENABLED: false,
|
||||||
|
BASE_API: "https://strangeapi.hostz.me/api",
|
||||||
|
},
|
||||||
|
|
||||||
|
INVITE: {
|
||||||
|
ENABLED: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
MODERATION: {
|
||||||
|
ENABLED: false,
|
||||||
|
EMBED_COLORS: {
|
||||||
|
TIMEOUT: "#102027",
|
||||||
|
UNTIMEOUT: "#4B636E",
|
||||||
|
KICK: "#FF7961",
|
||||||
|
SOFTBAN: "#AF4448",
|
||||||
|
BAN: "#D32F2F",
|
||||||
|
UNBAN: "#00C853",
|
||||||
|
VMUTE: "#102027",
|
||||||
|
VUNMUTE: "#4B636E",
|
||||||
|
DEAFEN: "#102027",
|
||||||
|
UNDEAFEN: "#4B636E",
|
||||||
|
DISCONNECT: "RANDOM",
|
||||||
|
MOVE: "RANDOM",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
PRESENCE: {
|
||||||
|
ENABLED: true, // Whether or not the bot should update its status
|
||||||
|
STATUS: "online", // The bot's status [online, idle, dnd, invisible]
|
||||||
|
TYPE: "LISTENING", // Status type for the bot [PLAYING | LISTENING | WATCHING | COMPETING]
|
||||||
|
MESSAGE: "/play with {members} members in {servers} servers", // Your bot status message
|
||||||
|
},
|
||||||
|
|
||||||
|
STATS: {
|
||||||
|
ENABLED: false,
|
||||||
|
XP_COOLDOWN: 5, // Cooldown in seconds between messages
|
||||||
|
DEFAULT_LVL_UP_MSG: "{member:tag}, You just advanced to **Level {level}**",
|
||||||
|
},
|
||||||
|
|
||||||
|
SUGGESTIONS: {
|
||||||
|
ENABLED: false, // Should the suggestion system be enabled
|
||||||
|
EMOJI: {
|
||||||
|
UP_VOTE: "⬆️",
|
||||||
|
DOWN_VOTE: "⬇️",
|
||||||
|
},
|
||||||
|
DEFAULT_EMBED: "#4F545C",
|
||||||
|
APPROVED_EMBED: "#43B581",
|
||||||
|
DENIED_EMBED: "#F04747",
|
||||||
|
},
|
||||||
|
|
||||||
|
TICKET: {
|
||||||
|
ENABLED: false,
|
||||||
|
CREATE_EMBED: "#068ADD",
|
||||||
|
CLOSE_EMBED: "#068ADD",
|
||||||
|
},
|
||||||
};
|
};
|
@ -7,6 +7,8 @@ module.exports.launch = async (client) => {
|
|||||||
|
|
||||||
const express = require("express"),
|
const express = require("express"),
|
||||||
session = require("express-session"),
|
session = require("express-session"),
|
||||||
|
MongoStore = require("connect-mongo"),
|
||||||
|
mongoose = require("@src/database/mongoose"),
|
||||||
path = require("path"),
|
path = require("path"),
|
||||||
app = express();
|
app = express();
|
||||||
|
|
||||||
@ -19,6 +21,8 @@ module.exports.launch = async (client) => {
|
|||||||
client.states = {};
|
client.states = {};
|
||||||
client.config = config;
|
client.config = config;
|
||||||
|
|
||||||
|
const db = await mongoose.initializeMongoose();
|
||||||
|
|
||||||
/* App configuration */
|
/* App configuration */
|
||||||
app
|
app
|
||||||
.use(express.json()) // For post methods
|
.use(express.json()) // For post methods
|
||||||
@ -28,7 +32,23 @@ module.exports.launch = async (client) => {
|
|||||||
.use(express.static(path.join(__dirname, "/public"))) // Set the css and js folder to ./public
|
.use(express.static(path.join(__dirname, "/public"))) // Set the css and js folder to ./public
|
||||||
.set("views", path.join(__dirname, "/views")) // Set the ejs templates to ./views
|
.set("views", path.join(__dirname, "/views")) // Set the ejs templates to ./views
|
||||||
.set("port", config.DASHBOARD.port) // Set the dashboard port
|
.set("port", config.DASHBOARD.port) // Set the dashboard port
|
||||||
.use(session({ secret: process.env.SESSION_PASSWORD, resave: false, saveUninitialized: false })) // Set the express session password and configuration
|
.use(
|
||||||
|
session({
|
||||||
|
secret: process.env.SESSION_PASSWORD,
|
||||||
|
cookie: { maxAge: 336 * 60 * 60 * 1000 },
|
||||||
|
name: "djs_connection_cookie",
|
||||||
|
resave: true,
|
||||||
|
saveUninitialized: false,
|
||||||
|
store: MongoStore.create({
|
||||||
|
client: db.getClient(),
|
||||||
|
dbName: db.name,
|
||||||
|
collectionName: "sessions",
|
||||||
|
stringify: false,
|
||||||
|
autoRemove: "interval",
|
||||||
|
autoRemoveInterval: 1,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
) // Set the express session password and configuration
|
||||||
.use(async function (req, res, next) {
|
.use(async function (req, res, next) {
|
||||||
req.user = req.session.user;
|
req.user = req.session.user;
|
||||||
req.client = client;
|
req.client = client;
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 588 B After Width: | Height: | Size: 588 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |