3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-03-15 13:51:51 +00:00
zeppelin/src/utils.ts
2018-12-22 12:39:03 +02:00

283 lines
7.1 KiB
TypeScript

import at = require("lodash.at");
import { Guild, GuildAuditLogEntry } from "eris";
import url from "url";
import tlds from "tlds";
import emojiRegex from "emoji-regex";
/**
* Turns a "delay string" such as "1h30m" to milliseconds
* @param {String} str
* @returns {Number}
*/
export function convertDelayStringToMS(str) {
const regex = /^([0-9]+)\s*([dhms])?[a-z]*\s*/;
let match;
let ms = 0;
str = str.trim();
// tslint:disable-next-line
while (str !== "" && (match = str.match(regex)) !== null) {
if (match[2] === "d") ms += match[1] * 1000 * 60 * 60 * 24;
else if (match[2] === "h") ms += match[1] * 1000 * 60 * 60;
else if (match[2] === "s") ms += match[1] * 1000;
else if (match[2] === "m" || !match[2]) ms += match[1] * 1000 * 60;
str = str.slice(match[0].length);
}
// Invalid delay string
if (str !== "") {
return null;
}
return ms;
}
export function successMessage(str) {
return `👌 ${str}`;
}
export function errorMessage(str) {
return `${str}`;
}
export function uclower(str) {
return str[0].toLowerCase() + str.slice(1);
}
export function stripObjectToScalars(obj, includedNested: string[] = []) {
const result = {};
for (const key in obj) {
if (
obj[key] == null ||
typeof obj[key] === "string" ||
typeof obj[key] === "number" ||
typeof obj[key] === "boolean"
) {
result[key] = obj[key];
} else if (typeof obj[key] === "object") {
const prefix = `${key}.`;
const nestedNested = includedNested
.filter(p => p === key || p.startsWith(prefix))
.map(p => (p === key ? p : p.slice(prefix.length)));
if (nestedNested.length) {
result[key] = stripObjectToScalars(obj[key], nestedNested);
}
}
}
return result;
}
const stringFormatRegex = /{([^{}]+?)}/g;
export function formatTemplateString(str: string, values) {
return str.replace(stringFormatRegex, (match, prop) => {
const value = at(values, prop)[0];
return typeof value === "string" || typeof value === "number" ? String(value) : "";
});
}
export const snowflakeRegex = /[1-9][0-9]{5,19}/;
const isSnowflakeRegex = new RegExp(`^${snowflakeRegex}$`);
export function isSnowflake(v: string): boolean {
return isSnowflakeRegex.test(v);
}
export function sleep(ms: number): Promise<void> {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
/**
* Attempts to find a relevant audit log entry for the given user and action
*/
export async function findRelevantAuditLogEntry(
guild: Guild,
actionType: number,
userId: string,
attempts: number = 3,
attemptDelay: number = 3000
): Promise<GuildAuditLogEntry> {
const auditLogEntries = await guild.getAuditLogs(5, null, actionType);
auditLogEntries.entries.sort((a, b) => {
if (a.createdAt > b.createdAt) return -1;
if (a.createdAt > b.createdAt) return 1;
return 0;
});
const cutoffTS = Date.now() - 1000 * 60 * 2;
const relevantEntry = auditLogEntries.entries.find(entry => {
return entry.targetID === userId && entry.createdAt >= cutoffTS;
});
if (relevantEntry) {
return relevantEntry;
} else if (attempts > 0) {
await sleep(attemptDelay);
return findRelevantAuditLogEntry(guild, actionType, userId, attempts - 1, attemptDelay);
} else {
return null;
}
}
const urlRegex = /(\S+\.\S+)/g;
const protocolRegex = /^[a-z]+:\/\//;
export function getUrlsInString(str: string): url.URL[] {
const matches = str.match(urlRegex) || [];
return matches.reduce((urls, match) => {
if (!protocolRegex.test(match)) {
match = `https://${match}`;
}
let matchUrl: url.URL;
try {
matchUrl = new url.URL(match);
} catch (e) {
return urls;
}
const hostnameParts = matchUrl.hostname.split(".");
const tld = hostnameParts[hostnameParts.length - 1];
if (tlds.includes(tld)) {
urls.push(matchUrl);
}
return urls;
}, []);
}
export function getInviteCodesInString(str: string): string[] {
const inviteCodeRegex = /(?:discord.gg|discordapp.com\/invite)\/([a-z0-9]+)/gi;
const inviteCodes = [];
let match;
// tslint:disable-next-line
while ((match = inviteCodeRegex.exec(str)) !== null) {
inviteCodes.push(match[1]);
}
return inviteCodes;
}
export const unicodeEmojiRegex = emojiRegex();
export const customEmojiRegex = /<:(?:.*?):(\d+)>/;
export const anyEmojiRegex = new RegExp(`(?:(?:${unicodeEmojiRegex.source})|(?:${customEmojiRegex.source}))`);
const matchAllEmojiRegex = new RegExp(anyEmojiRegex.source, "g");
export function getEmojiInString(str: string): string[] {
return str.match(matchAllEmojiRegex) || [];
}
export function trimLines(str: string) {
return str
.trim()
.split("\n")
.map(l => l.trim())
.join("\n")
.trim();
}
export const emptyEmbedValue = "\u200b";
export const embedPadding = "\n" + emptyEmbedValue;
export const userMentionRegex = /<@!?([0-9]+)>/g;
export const roleMentionRegex = /<&([0-9]+)>/g;
export function getUserMentions(str: string) {
const regex = new RegExp(userMentionRegex.source, "g");
const userIds = [];
let match;
// tslint:disable-next-line
while ((match = regex.exec(str)) !== null) {
userIds.push(match[1]);
}
return userIds;
}
export function getRoleMentions(str: string) {
const regex = new RegExp(roleMentionRegex.source, "g");
const roleIds = [];
let match;
// tslint:disable-next-line
while ((match = regex.exec(str)) !== null) {
roleIds.push(match[1]);
}
return roleIds;
}
/**
* Disables link previews in the given string by wrapping links in < >
*/
export function disableLinkPreviews(str: string): string {
return str.replace(/(?<!\<)(https?:\/\/\S+)/gi, "<$1>");
}
export function deactivateMentions(content: string): string {
return content.replace(/@/g, "@\u200b");
}
export function disableCodeBlocks(content: string): string {
return content.replace(/`/g, "`\u200b");
}
export function useMediaUrls(content: string): string {
return content.replace(/cdn\.discordapp\.com/g, "media.discordapp.net");
}
export function chunkLines(str: string, maxChunkLength = 2000): string[] {
if (str.length < maxChunkLength) {
return [str];
}
const chunks = [];
while (str.length) {
if (str.length <= maxChunkLength) {
chunks.push(str);
break;
}
const slice = str.slice(0, maxChunkLength);
const lastLineBreakIndex = slice.lastIndexOf("\n");
if (lastLineBreakIndex === -1) {
chunks.push(str.slice(0, maxChunkLength));
str = str.slice(maxChunkLength);
} else {
chunks.push(str.slice(0, lastLineBreakIndex));
str = str.slice(lastLineBreakIndex + 1);
}
}
return chunks;
}
/**
* Chunks a long message to multiple smaller messages, retaining leading and trailing line breaks
*/
export function chunkMessageLines(str: string): string[] {
const chunks = chunkLines(str, 1999);
return chunks.map(chunk => {
if (chunk[0] === "\n") chunk = "\u200b" + chunk;
if (chunk[chunk.length - 1] === "\n") chunk = chunk + "\u200b";
return chunk;
});
}
export function noop() {
// IT'S LITERALLY NOTHING
}
export const DBDateFormat = "YYYY-MM-DD HH:mm:ss";