Merge branch 'master' of https://github.com/Navy-gif/New-Gbot
This commit is contained in:
commit
fdb31aa0f4
@ -36,6 +36,7 @@
|
||||
"request": "^2.88.2",
|
||||
"similarity": "^1.2.1",
|
||||
"timestring": "^6.0.0",
|
||||
"twemoji-parser": "^13.1.0",
|
||||
"winston": "^3.2.1",
|
||||
"winston-transport": "^4.3.0"
|
||||
},
|
||||
|
@ -16,9 +16,10 @@ class RateLimiter {
|
||||
this.deleteTimeouts = {}; //same as above
|
||||
|
||||
this.lastSend = {}; //used by limitSend
|
||||
this.lastDelete = {};
|
||||
|
||||
this.sendInterval = 7.5; //How frequently sending is allowed in seconds
|
||||
this.deleteInterval = 1.5; //How frequently delete queues should be executed
|
||||
this.deleteInterval = 2.5; //How frequently delete queues should be executed
|
||||
|
||||
}
|
||||
|
||||
@ -41,7 +42,8 @@ class RateLimiter {
|
||||
if(!this.deleteQueue[channel.id]) this.deleteQueue[channel.id] = [];
|
||||
this.deleteQueue[channel.id].push({ message, resolve, reject });
|
||||
|
||||
if(!this.deleteTimeouts[channel.id] || this.deleteTimeouts[channel.id]._destroyed) this.deleteTimeouts[channel.id] = setTimeout(this.delete.bind(this), this.deleteInterval*1000, channel);
|
||||
//if(!this.deleteTimeouts[channel.id] || this.deleteTimeouts[channel.id]._destroyed) this.deleteTimeouts[channel.id] = setTimeout(this.delete.bind(this), this.deleteInterval*1000, channel);
|
||||
this.delete(channel);
|
||||
|
||||
});
|
||||
|
||||
@ -56,14 +58,23 @@ class RateLimiter {
|
||||
queue = [...this.deleteQueue[channel.id]],
|
||||
deleteThese = [];
|
||||
|
||||
for(const item of queue) {
|
||||
const lastDelete = this.lastDelete[channel.id];
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
if (now - lastDelete < this.deleteInterval) {
|
||||
const timeout = this.deleteTimeouts[channel.id];
|
||||
if (!timeout || timeout._destroyed) this.deleteTimeouts[channel.id] = setTimeout(this.delete.bind(this), this.deleteInterval*1000, channel);
|
||||
return;
|
||||
}
|
||||
this.lastDelete[channel.id] = now;
|
||||
|
||||
for(const item of queue) { // Organise into arrays
|
||||
const { message, resolve, reject } = item;
|
||||
if(deleteThese.length <= 100) {
|
||||
deleteThese.push(message);
|
||||
resolves.push(resolve);
|
||||
rejects.push(reject);
|
||||
this.deleteQueue[channel.id].shift();
|
||||
} else {
|
||||
} else { // left over messages go in next batch
|
||||
this.deleteTimeouts[channel.id] = setTimeout(this.delete.bind(this), this.deleteInterval*1000, channel);
|
||||
break;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
const timestring = require('timestring');
|
||||
const moment = require('moment');
|
||||
const dns = require('dns');
|
||||
const { parse: resolveTwemoji } = require('twemoji-parser');
|
||||
|
||||
const { InfractionResolves } = require('../../util/Constants.js');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
@ -593,10 +594,10 @@ class Resolver {
|
||||
const { roles } = guild;
|
||||
const resolved = [];
|
||||
|
||||
for(const resolveable of resolveables) {
|
||||
|
||||
const id = /^(<@&)?([0-9]{16,22})>?/iu;
|
||||
|
||||
for(const resolveable of resolveables) {
|
||||
|
||||
if(id.test(resolveable)) {
|
||||
|
||||
const match = resolveable.match(id);
|
||||
@ -711,6 +712,74 @@ class Resolver {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves input into an emoji, either a custom or a unicode emoji. Returned object will have a type property indicating whether it's custom or unicode.
|
||||
*
|
||||
* @param {Array<String>} [resolveables=[]]
|
||||
* @param {boolean} strict
|
||||
* @param {Guild} guild
|
||||
* @return {Object}
|
||||
* @memberof Resolver
|
||||
*/
|
||||
async resolveEmojis(resolveables = [], strict, guild) {
|
||||
|
||||
if (typeof resolveables === 'string') resolveables = [resolveables];
|
||||
if (resolveables.length === 0) return false;
|
||||
//const { emojis: guildEmojis } = guild;
|
||||
const { emojis: clientEmojis } = this.client;
|
||||
const resolved = [];
|
||||
|
||||
const emojiMention = /<(?:a)?:?([\w-]{2,32}):(\d{17,32})>/iu;
|
||||
|
||||
for (const resolveable of resolveables) {
|
||||
|
||||
if (emojiMention.test(resolveable)) {
|
||||
|
||||
const match = resolveable.match(emojiMention);
|
||||
const [, , eId] = match;
|
||||
|
||||
const emoji = clientEmojis.resolve(eId); //.catch(this.client.logger.error); // use .fetch(eId) once v13 rolls out
|
||||
|
||||
if (emoji) resolved.push({ type: 'custom', emoji });
|
||||
|
||||
} else {
|
||||
|
||||
const sorter = (a, b) => a.name.length - b.name.length;
|
||||
const filter = (e) => {
|
||||
if (!strict) return e.name.toLowerCase().includes(resolveable.toLowerCase());
|
||||
return e.name.toLowerCase() === resolveable.toLowerCase();
|
||||
};
|
||||
let emoji = null;
|
||||
if (guild) emoji = guild.emojis.cache.sort(sorter).filter(filter).first();
|
||||
if (!emoji) emoji = clientEmojis.cache.sort(sorter).filter(filter).first();
|
||||
if (emoji) {
|
||||
resolved.push({ type: 'custom', emoji });
|
||||
continue;
|
||||
} else { // twemoji parse
|
||||
const [result] = resolveTwemoji(resolveable);
|
||||
if (result) {
|
||||
({ text: emoji } = result);
|
||||
resolved.push({ type: 'unicode', emoji });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return resolved.length > 0 ? resolved : false;
|
||||
|
||||
}
|
||||
|
||||
async resolveEmoji(resolveable, strict, guild) {
|
||||
|
||||
if (!resolveable) return false;
|
||||
if (resolveable instanceof Array) throw new Error('Resolveable cannot be of type Array, use resolveEmojis for resolving arrays of emojis');
|
||||
const result = await this.resolveEmojis([resolveable], strict, guild);
|
||||
return result ? result[0] : false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Resolver;
|
@ -82,7 +82,7 @@ class StatsCommand extends Command {
|
||||
});
|
||||
return acc;
|
||||
}, {});
|
||||
totalValues.uptime = this.client.resolver.timeAgo(Math.floor(totalValues.uptime / 1000), true, true, true);
|
||||
totalValues.uptime = this.client.resolver.timeAgo(Math.floor(totalValues.uptime/ shard.count / 1000), true, true, true);
|
||||
totalValues.avgMem = Math.floor(totalValues.memory / shard.count);
|
||||
|
||||
//System information
|
||||
|
@ -182,12 +182,23 @@ module.exports = class AutoModeration extends Observer {
|
||||
|
||||
for (const reg of regex) {
|
||||
|
||||
const match = content.match(new RegExp(reg, 'iu'));
|
||||
const match = content.match(new RegExp(`(?:^|\\s)(${reg})`, 'iu')); // (?:^|\\s) |un
|
||||
|
||||
if (match) {
|
||||
log += `\nMessage matched with "${reg}" in the regex list.\nMatch: ${match[0]}\nFull content: ${content}`;
|
||||
//log += `\next reg: ${tmp}`;
|
||||
const fullWord = words.find((word) => word.includes(match[1]));
|
||||
|
||||
let inWL = false;
|
||||
try { // This is for debugging only
|
||||
inWL = this.whitelist.find(fullWord);
|
||||
} catch (err) {
|
||||
this.client.logger.debug(fullWord, match[0], words);
|
||||
}
|
||||
if (inWL || whitelist.some((word) => word === fullWord)) continue;
|
||||
|
||||
log += `\nMessage matched with "${reg}" in the regex list.\nMatch: ${match[0]}, Full word: ${fullWord}\nFull content: ${content}`;
|
||||
filterResult = {
|
||||
match: match[0],
|
||||
match: fullWord,
|
||||
matched: true,
|
||||
_matcher: match[0].toLowerCase(),
|
||||
matcher: `Regex: __${reg}__`,
|
||||
@ -310,7 +321,7 @@ module.exports = class AutoModeration extends Observer {
|
||||
|
||||
for (const reg of words) {
|
||||
|
||||
match = content.match(new RegExp(reg, 'iu'));
|
||||
match = content.match(new RegExp(`(?:^|\\s)(${reg})`, 'iu'));
|
||||
|
||||
if (match) break;
|
||||
|
||||
@ -326,7 +337,7 @@ module.exports = class AutoModeration extends Observer {
|
||||
`, // ** User:** <@${ author.id }>
|
||||
color: 15120384,
|
||||
fields: context.reverse().reduce((acc, val) => {
|
||||
const text = val.content.length ? val.content.replace(match, '**__$&__**') : '**NO CONTENT**';
|
||||
const text = val.content.length ? Util.escapeMarkdown(val.content).replace(match[1], '**__$&__**') : '**NO CONTENT**';
|
||||
acc.push({
|
||||
name: `${val.author.tag} (${val.author.id}) - ${val.id}`,
|
||||
value: text.length < 1024 ? text : text.substring(0, 1013) + '...'
|
||||
@ -339,7 +350,9 @@ module.exports = class AutoModeration extends Observer {
|
||||
}, [])
|
||||
};
|
||||
|
||||
logChannel.send({ embed });
|
||||
const sent = await logChannel.send({ embed }).catch((err) => {
|
||||
this.client.logger.error('Error in message flag:\n' + err.stack);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
@ -389,6 +389,7 @@ class CommandHandler extends Observer {
|
||||
const silent = inhibitors.filter((i) => i.inhibitor.silent);
|
||||
const nonsilent = inhibitors.filter((i) => !i.inhibitor.silent);
|
||||
|
||||
if (silent.length && silent.some((result) => result.inhibitor.id === 'channelIgnore')) return;
|
||||
if (nonsilent.length === 0 && silent.length > 0) return undefined;
|
||||
if (nonsilent.length > 0) return this.handleError(message, { type: 'inhibitor', ...nonsilent[0] });
|
||||
|
||||
@ -428,7 +429,7 @@ class CommandHandler extends Observer {
|
||||
const reasons = (await Promise.all(promises)).filter((p) => p.error); // Filters out inhibitors with only errors.
|
||||
if(reasons.length === 0) return [];
|
||||
|
||||
reasons.sort((a, b) => b.inhibitor.priority - a.inhibitor.priority); // Sorts inhibitor errors by most important.
|
||||
reasons.sort((a, b) => a.inhibitor.priority - b.inhibitor.priority); // Sorts inhibitor errors by most important.
|
||||
return reasons;
|
||||
|
||||
}
|
||||
|
@ -72,7 +72,11 @@ class GuildLogger extends Observer {
|
||||
|
||||
await message.guild.settings();
|
||||
|
||||
if (!message.member) message.member = await message.guild.members.fetch(message.author.id).catch();
|
||||
if (!message.member) try {
|
||||
message.member = await message.guild.members.fetch(message.author.id);
|
||||
} catch (_) {
|
||||
// Member not found, do nothing
|
||||
}
|
||||
|
||||
const { messageLog } = message.guild._settings;
|
||||
if(!messageLog.channel) return undefined;
|
||||
@ -99,30 +103,42 @@ class GuildLogger extends Observer {
|
||||
return;
|
||||
}
|
||||
|
||||
const { reference, channel, author, content, id } = message;
|
||||
|
||||
const embed = {
|
||||
// author: {
|
||||
// name: message.format('MSGLOG_DELETE_TITLE', { channel: message.channel.name, author: Util.escapeMarkdown(message.author.tag) }), //`${message.author.tag} (${message.author.id})`,
|
||||
// icon_url: message.author.displayAvatarURL({ size: 32 }) //eslint-disable-line camelcase
|
||||
// },
|
||||
title: message.format('MSGLOG_DELETE_TITLE', { channel: message.channel.name, author: Util.escapeMarkdown(message.author.tag) }),
|
||||
description: Util.escapeMarkdown(message.content)?.replace(/\\n/gu, ' ') || message.format('MSGLOG_NOCONTENT'),
|
||||
title: message.format('MSGLOG_DELETE_TITLE', { channel: channel.name, author: Util.escapeMarkdown(author.tag) }),
|
||||
description: Util.escapeMarkdown(content)?.replace(/\\n/gu, ' ') || message.format('MSGLOG_NOCONTENT'),
|
||||
color: CONSTANTS.COLORS.RED,
|
||||
footer: {
|
||||
text: message.format('MSGLOG_DELETE_FOOTER', { msgID: message.id, userID: message.author.id })
|
||||
text: message.format('MSGLOG_DELETE_FOOTER', { msgID: id, userID: author.id })
|
||||
},
|
||||
timestamp: message.createdAt
|
||||
timestamp: message.createdAt,
|
||||
fields: []
|
||||
};
|
||||
|
||||
if (reference && reference.channelID === channel.id) {
|
||||
const referenced = await channel.messages.fetch(reference.messageID);
|
||||
embed.fields.push({
|
||||
name: message.format('MSGLOG_REPLY', { tag: referenced.author.tag, id: referenced.author.id }),
|
||||
value: message.format('MSGLOG_REPLY_VALUE', {
|
||||
content: referenced.content.length > 900 ? referenced.content.substring(0, 900) + '...' : referenced.content,
|
||||
link: referenced.url
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (message.filtered) {
|
||||
embed.fields = [
|
||||
{
|
||||
embed.fields.push({
|
||||
name: message.format('MSGLOG_FILTERED'),
|
||||
value: stripIndents`
|
||||
${message.format(message.filtered.preset ? 'MSGLOG_FILTERED_PRESET' : 'MSGLOG_FILTERED_VALUE', { ...message.filtered })}
|
||||
${message.filtered.sanctioned ? message.format('MSGLOG_FILTERED_SANCTIONED') : ''}
|
||||
`// + ()
|
||||
}
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
const uploadedFiles = [];
|
||||
@ -203,7 +219,9 @@ class GuildLogger extends Observer {
|
||||
|
||||
}
|
||||
|
||||
hook.send({ embeds: [embed], files: uploadedFiles });
|
||||
await hook.send({ embeds: [embed], files: uploadedFiles }).catch((err) => {
|
||||
this.client.logger.error('Error in message delete:\n' + err.stack);
|
||||
});
|
||||
|
||||
/*
|
||||
if(message.attachments.size > 0) {
|
||||
@ -337,7 +355,7 @@ class GuildLogger extends Observer {
|
||||
if (!guild) return;
|
||||
|
||||
if (!oldMessage.member) oldMessage.member = await guild.members.fetch(oldMessage.author);
|
||||
const { member, channel, author } = oldMessage;
|
||||
const { member, channel, author, reference } = oldMessage;
|
||||
|
||||
const settings = await guild.settings();
|
||||
const chatlogs = settings.messageLog;
|
||||
@ -386,16 +404,7 @@ class GuildLogger extends Observer {
|
||||
description: oldMessage.format('MSGLOG_EDIT_JUMP', { guild: guild.id, channel: channel.id, message: oldMessage.id }),
|
||||
color: CONSTANTS.COLORS.YELLOW,
|
||||
timestamp: oldMessage.createdAt,
|
||||
fields: [
|
||||
// {
|
||||
// name: oldMessage.format('MSGLOG_EDIT_OLD'),
|
||||
// value: oldMessage.content.length > 1024 ? oldMessage.content.substring(0, 1021) + '...' : oldMessage.content
|
||||
// },
|
||||
// {
|
||||
// name: oldMessage.format('MSGLOG_EDIT_NEW'),
|
||||
// value: newMessage.content.length > 1024 ? newMessage.content.substring(0, 1021) + '...' : newMessage.content
|
||||
// }
|
||||
]
|
||||
fields: [ ]
|
||||
};
|
||||
|
||||
const oldCon = oldMessage.content,
|
||||
@ -420,6 +429,19 @@ class GuildLogger extends Observer {
|
||||
value: '...' + newCon.substring(1021)
|
||||
});
|
||||
|
||||
if (reference && reference.channelID === channel.id) {
|
||||
const referenced = await channel.messages.fetch(reference.messageID);
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
const content = referenced.content ? referenced.content.length > 900 ? referenced.content.substring(0, 900) + '...' : referenced.content : oldMessage.format('MSGLOG_REPLY_NOCONTENT');
|
||||
embed.fields.push({
|
||||
name: oldMessage.format('MSGLOG_REPLY', { tag: referenced.author.tag, id: referenced.author.id }),
|
||||
value: oldMessage.format('MSGLOG_REPLY_VALUE', {
|
||||
content,
|
||||
link: referenced.url
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
//if(oldMessage.content.length > 1024) embed.description += '\n' + oldMessage.format('MSGLOG_EDIT_OLD_CUTOFF');
|
||||
//if(newMessage.content.length > 1024) embed.description += '\n' + oldMessage.format('MSGLOG_EDIT_NEW_CUTOFF');
|
||||
|
||||
|
@ -120,6 +120,11 @@ module.exports = class InviteFilter extends FilterSetting {
|
||||
|
||||
}
|
||||
|
||||
if (langParams.error) return {
|
||||
error: true,
|
||||
msg: langParams.msg
|
||||
};
|
||||
|
||||
await message.guild._updateSettings({ [this.index]: setting });
|
||||
return {
|
||||
error: false,
|
||||
|
@ -202,6 +202,11 @@ module.exports = class LinkFilter extends FilterSetting {
|
||||
msg: message.format('ERR_INVALID_METHOD', { method })
|
||||
};
|
||||
|
||||
if (langParams.error) return {
|
||||
error: true,
|
||||
msg: langParams.msg
|
||||
};
|
||||
|
||||
await message.guild._updateSettings({ [this.index]: setting });
|
||||
return {
|
||||
error: false,
|
||||
|
@ -149,6 +149,11 @@ module.exports = class MentionFilter extends FilterSetting {
|
||||
msg: message.format('ERR_INVALID_METHOD', { method })
|
||||
};
|
||||
|
||||
if (langParams.error) return {
|
||||
error: true,
|
||||
msg: langParams.msg
|
||||
};
|
||||
|
||||
await message.guild._updateSettings({ [this.index]: setting });
|
||||
return {
|
||||
error: false,
|
||||
|
@ -53,6 +53,7 @@ module.exports = class WordFilter extends FilterSetting {
|
||||
|
||||
async handle(message, params) {
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
let [method, ...args] = params;
|
||||
method = method.toLowerCase();
|
||||
|
||||
@ -254,6 +255,11 @@ module.exports = class WordFilter extends FilterSetting {
|
||||
};
|
||||
}
|
||||
|
||||
if (langParams.error) return {
|
||||
error: true,
|
||||
msg: langParams.msg
|
||||
};
|
||||
|
||||
await message.guild._updateSettings({ [this.index]: setting });
|
||||
return {
|
||||
error: false,
|
||||
@ -263,6 +269,7 @@ module.exports = class WordFilter extends FilterSetting {
|
||||
}
|
||||
|
||||
async _createTrigger(message, action, actionObject, setting) {
|
||||
|
||||
const response = await message.prompt(message.format('S_WORDFILTER_ACTION_ADD_TRIGGERS'), { time: 60 * 1000 });
|
||||
if (!response) {
|
||||
if (setting.actions.find((ac) => ac.trigger === 'generic')) return {
|
||||
|
@ -64,7 +64,7 @@ class BinaryTree {
|
||||
*/
|
||||
find(val) {
|
||||
|
||||
val.toLowerCase();
|
||||
val = val.toLowerCase();
|
||||
|
||||
if(this.isEmpty()) return;
|
||||
|
||||
|
@ -72,6 +72,16 @@ Link filter violation.
|
||||
[MSGLOG_NOCONTENT]
|
||||
**__NO TEXT CONTENT__**
|
||||
|
||||
[MSGLOG_REPLY]
|
||||
Message was in reply to user {tag} ({id}):
|
||||
|
||||
[MSGLOG_REPLY_VALUE]
|
||||
**[Jump to message]({link})**
|
||||
{content}
|
||||
|
||||
[MSGLOG_REPLY_NOCONTENT]
|
||||
**__Missing content.__**
|
||||
|
||||
[MSGLOG_FILTERED]
|
||||
The message was filtered:
|
||||
|
||||
|
@ -52,6 +52,8 @@ Can be one of `{valid}`.
|
||||
|
||||
> You can cancel this series of prompts by responding with `cancel`.
|
||||
|
||||
{wordwatcher}
|
||||
|
||||
[S_FILTER_ACTION_ADD_TIMER]
|
||||
Would you like the **{action}** to have a timer?
|
||||
Not assigning a timer will use defaults for the corresponding action.
|
||||
@ -219,6 +221,11 @@ Successfully removed all presets from the regex filter.
|
||||
[S_WORDWATCHER_DESCRIPTION]
|
||||
Configure the behaviour of the word watcher.
|
||||
|
||||
Wordwatcher is a moderation utility that flags messages for manual review based on keywords.
|
||||
Keywords can be regex expressions.
|
||||
|
||||
Wordwatcher also supports having 5 reactions for quick actions.
|
||||
|
||||
[S_WORDWATCHER_TOGGLE]
|
||||
Successfully toggled the word watcher **{toggle}**.
|
||||
|
||||
@ -261,6 +268,18 @@ Successfully removed **{changes}** from the watch list.
|
||||
[S_WORDWATCHER_CHANNEL]
|
||||
Will log flagged messages to <#{channel}>.
|
||||
|
||||
[S_WORDWATCHER_ACTION_LIMIT]
|
||||
You've hit the limit of quick actions. Either modify or remove existing ones.
|
||||
|
||||
[S_WORDWATCHER_ACTION_ADD_START]
|
||||
You can only define up to 5 quick actions, you currently have {amount} existing actions.
|
||||
|
||||
[S_WORDWATCHER_ACTION_ADD_TRIGGERS]
|
||||
Which emoji should represent this action?
|
||||
Make sure it is one that the bot has access to (i.e. from this server or one you know the bot is in).
|
||||
|
||||
The bot will use defaults if no emoji is given.
|
||||
|
||||
// Wordfilter
|
||||
[S_WORDFILTER_DESCRIPTION]
|
||||
Configure the word filtering behaviour for your server.
|
||||
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"regex": {
|
||||
"slurs": [
|
||||
"n(ae|ji|j|y|i|x|!|1|\\||l)(gg?|qq|99?|bb)(?!(ht|el|un))((e|3)r|let|ur|\\s?nog|y|ah?|or)?s?",
|
||||
"(?<!u)niqa?",
|
||||
"n(ae|ji|j|y|i|x|!|1|\\||l)(gg?|qq?|99?|bb)((e|3)r|let|ur|\\s?nog|y|ah?|or)?s?",
|
||||
"nick\\s?(gurr?|ger|ga)",
|
||||
"(fur\\s?)?f(e|a|4|x)(gg?|qq|99?)(otry|ots|ot|y|s)?",
|
||||
"(fur\\s?)?fgg?ts?",
|
||||
@ -500,8 +499,12 @@
|
||||
"blobsob",
|
||||
"poggers",
|
||||
"night",
|
||||
"nightmare",
|
||||
"nightmares",
|
||||
"nightcore",
|
||||
"pingers",
|
||||
"nigeria",
|
||||
"nigel",
|
||||
"negative",
|
||||
"neglect",
|
||||
"initiative",
|
||||
@ -560,6 +563,7 @@
|
||||
"determination",
|
||||
"tenis",
|
||||
"nights",
|
||||
"nights?",
|
||||
"poin",
|
||||
"alteration",
|
||||
"mock",
|
||||
|
@ -2350,6 +2350,11 @@ tweetnacl@^1.0.3:
|
||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
|
||||
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
|
||||
|
||||
twemoji-parser@^13.1.0:
|
||||
version "13.1.0"
|
||||
resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-13.1.0.tgz#65e7e449c59258791b22ac0b37077349127e3ea4"
|
||||
integrity sha512-AQOzLJpYlpWMy8n+0ATyKKZzWlZBJN+G0C+5lhX7Ftc2PeEVdUU/7ns2Pn2vVje26AIZ/OHwFoUbdv6YYD/wGg==
|
||||
|
||||
type-check@^0.4.0, type-check@~0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
|
||||
|
Loading…
Reference in New Issue
Block a user