You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1577 lines
52 KiB

const path = require('path');
const find = require('findit');
const {Client,ClientOptions,GatewayIntentBits,Message, Partials, ActivityType,EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, PermissionsBitField, PermissionFlagsBits } = require('discord.js')
const roleRulesM = require('./models/autoRoleRule');
const feedM = require('./models/feeds');
const xmlparser = require('xml-js')
const GuildM = require('./models/guilds');
const Spawner = require('child_process');
const mongoose = require('mongoose');
module.exports.GatewayIntentBits = GatewayIntentBits;
module.exports.Partials = Partials;
module.exports.ActivityType = ActivityType;
module.exports.EmbedBuilder = EmbedBuilder;
module.exports.ActionRowBuilder = ActionRowBuilder;
module.exports.ButtonBuilder = ButtonBuilder;
module.exports.ButtonStyle = ButtonStyle;
module.exports.Message = Message;
module.exports.PermissionsBitField = PermissionsBitField;
module.exports.PermissionFlagsBits = PermissionFlagsBits;
//@ts-check
class CommandOptions
{
/**
*
* @param {String} name
* @param {String[]} aliases
* @param {String} description
* @param {Boolean} needsAdmin
* @param {Boolean} hidden
*/
constructor (name, aliases, description, needsAdmin, hidden)
{
this.name = name;
this.aliases = aliases;
this.description = description;
this.needsAdmin = needsAdmin;
this.hidden = hidden;
}
}
class command
{
/**
* @param {_Client} client extends discord.js.Client
* @param {CommandOptions} options extends Lib.Options
*/
constructor(client, options)
{
this.name = options.name;
this.aliases = options.aliases;
this.description = options.description;
this.needsAdmin =options.needsAdmin;
this.hidden = options.hidden;
this.client = client;
}
}module.exports.Command = command;
class _Client extends Client
{
/**
*
* @param {ClientOptions} options
*/
commands = new Map();
constructor(options, mongoDBURI)
{
super(options);
let finder = find(path.resolve('commands'));
finder.on('file', file=>
{
let command = require(file);
if(typeof command === 'function')
{
let c = new command(this);
this.commands.set(c.name,{
needsAdmin: c.needsAdmin?c.needsAdmin:false,
command: command,
});
if(c.aliases)
{
for(var i =0; i<c.aliases.length+1; i++)
{
this.commands.set(c.aliases[i],{
needsAdmin: c.needsAdmin,
command: command,
});
}
}
}
})
finder.on('end', ()=>
{
this.enableCommands();
})
connectToDB();
function connectToDB()
{
try {
mongoose.connect(mongoDBURI);
} catch (err) {
console.log('Server: Error: There was an Error with the connection to the database, attempting to restart it.', err)
connectToDB();
}
}
mongoose.Promise = global.Promise;
this.setGuilds();
this.RoleSetter();
this.rustCommits = new rustCommits(this);
this.freegames = new FreeGames(this);
this.strikes = new Strikes(this);
this.on('disconnect', (event) => {
console.log(`Client disconnected: ${event.reason}`);
});
this.on('reconnecting', () => {
console.log('Client is reconnecting...');
});
this.YTFeed();
this.music();
setInterval(() => {
this.YTFeed()
this.music();
}, 60*60*1000);
}
setGuilds()
{
this.on('ready', ()=>
{
this.guilds.cache.forEach(guild=>
{
GuildM.find({gID:guild.id})
.then(
(guild_,err)=>
{
if(err || guild_.length==0)
{
var nGuild = GuildM();
nGuild.name = guild.name;
nGuild.memberCount = guild.members.cache.size;
nGuild.gID = guild.id;
nGuild.allowInvites = false;
nGuild.strikes = false;
nGuild.music = false;
nGuild.save(err=>
{
if(err) console.log('Server: Adding non existing guilds to the DB. Error ocurred:', err)
});
}
}
)
})
})
this.on('guildCreate', guild=>
{
var guildModel = new GuildM();
guildModel.name = guild.name;
guildModel.memberCount = guild.members.cache.size;
guildModel.gID = guild.id;
guildModel.allowInvites = false;
guildModel.strikes = false;
guildModel.music = false;
guildModel.save()
});
this.on('guildDelete', guild =>
{
GuildM.find({gID:guild.id}).then(guild=>
{
if(guild.length==0) return sendMessage(false, 'ID not on the list.')
GuildM.findOneAndRemove(guild.id, (err)=>
{
if(err) console.log('Server: Deleting guild from DB: There was an error:', err)
})
})
});
}
/**
* Register a group of commands under the folder commands
* @param {String} name - Name of the group.
* @param {String} folderName - Name of the folder.
*/
async enableCommands()
{
this.on("messageCreate", message=>
{
this.checkForInvites(message);
if(message.content.startsWith(process.env.prefix)) //Test prefix t!
{
let commandName=message.content.split('!')[1].split(' ')[0];
let args = message.content.split(' ');
args = args.slice(1);
args = args.filter(x => x !== '')
if(this.commands.get(commandName))
{
let needsAdmin = this.commands.get(commandName).needsAdmin;
let isAdmin =false;
try
{
isAdmin = this.guilds.cache.get(message.guild.id).members.cache.get(message.author.id).permissions.has(PermissionFlagsBits.Administrator);
} catch (error)
{
console.log('Lib: Command: Permission Verification: Failed')
}
let command = this.commands.get(commandName).command
if((needsAdmin && isAdmin) || !needsAdmin) return new command(this).run(message, args);
if(needsAdmin && !isAdmin) return new ErrorMessage(this).send(ErrorType.Permissions, message);
}
new ErrorMessage(this).send(ErrorType.NotOnTheList,message)
}
})
}
/**
*
* @param {String} name - Command Name
* @returns {command}
*/
async RoleSetter ()
{
this.on('messageReactionAdd', (reaction, user)=>
{
console.log('Lib: RoleSetter: Reaction: Added', reaction._emoji.name);
(async ()=>
{
const rule = await roleRulesM.findOne({mID:reaction.message.id,roleEmoji:reaction._emoji.id}).then(rule =>{return rule}).catch(err=>{return null})
if(rule)
{
console.log(`RoleAssignment: Guild:${this.guilds.cache.get(rule.gID)}: Adding role ${rule.roleName} given to ${user.username}`)
this.channels.cache.get(reaction.message.channelId).members.get(user.id).roles.add(rule.roleID)
}
})()
})
this.on('messageReactionRemove', (reaction, user)=>
{
(async ()=>
{
const rule = await roleRulesM.findOne({mID:reaction.message.id,roleEmoji:reaction._emoji.id}).then(rule =>{return rule}).catch(err=>{return null})
if(rule)
{
console.log(`RoleAssignment: Guild:${this.guilds.cache.get(rule.gID)}: Removing ${user.username}'s ${rule.roleName} role. `)
this.channels.cache.get(reaction.message.channelId).members.get(user.id).roles.remove(rule.roleID)
}
})()
})
}
async YTFeed()
{
const feeds = await feedM.find().then(feeds=>{return feeds})
if(feeds.length<1) return
for(var feed of feeds)
{
(async (feed)=>{
if(!feed.YTChannelId) return
if(feed.YTChannelId=='false') return
var res = await fetch(`https://www.youtube.com/feeds/videos.xml?channel_id=${feed.YTChannelId}`)
.then(handleResponse)
.then(handleData)
.catch(handleError);
function handleResponse(response)
{
return response.text()
}
function handleError(error)
{
return error;
}
function handleData(data)
{
data = xmlparser.xml2json(data,
{
compact: true,
space: 4
});
data = JSON.parse(data);
return {
link:data.feed.entry[0].link._attributes.href,
published:data.feed.entry[0].published._text,
link:data.feed.entry[0].link._attributes.href,
image:data.feed.entry[0].link._attributes.href.split('=')[1],
title:data.feed.entry[0].title._text,
author:
{
name:data.feed.entry[0].author.name._text,
url:data.feed.entry[0].author.uri._text
}
}
}
if(!res) return false;
const channel = this.channels.cache.get(feed.ChannelId);
if(!channel) return false;
var ytChannelName = await fetch('https://www.youtube.com/feeds/videos.xml?channel_id='+feed.YTChannelId,
{
method: "GET",
mode: "cors",
})
.then(response=>
{
if(response.ok) return response.text();
})
.then(data=>
{
data = xmlparser.xml2json(data,
{
compact: true,
space: 4
});
data = JSON.parse(data);
return data.feed.author.name._text
})
.catch(error=>
{
return error;
});
console.log('Lib: YTFeed: Channel:',ytChannelName,' | Discord Channel:', channel.name, ' | DCChannelId:',channel.id)
var aux = res;
const lastSentMessage= await channel.messages.fetch().then(res=>
{
for(var item of res)
{
if(item[1].embeds)
{
if(item[1].embeds[0])
{
var fields = item[1].embeds[0].fields.filter(x=>x.name === 'PublishedTimeStamp');
var isFeed = fields.length>0;
if(!aux.author) return null;
if(isFeed && item[1].embeds[0].title == aux.author.name) return item[1].embeds[0];
}
}
}
})
if(!lastSentMessage) return sendMessage(res, feed, channel);
const lastSentMessagePublished = lastSentMessage.fields.filter(x=>x.name === 'PublishedTimeStamp')[0].value;
// console.log(lastSentMessagePublished != res.published , 'lastSentPublished',lastSentMessagePublished,'published', res.published)
if(!lastSentMessagePublished) sendMessage(res, feed, channel)
else if(lastSentMessagePublished != res.published) sendMessage(res, feed, channel);
})(feed);
}
function sendMessage(res, feed, channel)
{
if(!res?.author?.name) return
console.log('Lib: YTFeed: SendMessage: Res: Author: Name:', res?.author?.name)
const embed = new EmbedBuilder()
embed.setFooter({text:'Rem-chan on ', iconURL:"https://i.imgur.com/g6FSNhL.png"})
embed.setAuthor({name:"Rem-chan", iconURL:"https://i.imgur.com/g6FSNhL.png",url:'https://rem.wordfights.com/addtodiscord'});
embed.setColor(0x110809);
embed.setTimestamp();
embed.setURL(res?.author?.url)
embed.setTitle(res?.author?.name);
embed.setImage(`https://i3.ytimg.com/vi/${res?.image}/maxresdefault.jpg`)
embed.setDescription(feed?.CostumMessage);
embed.addFields({name:res?.title, value:' '},
{name:'PublishedTimeStamp', value:res?.published},
{name:'Link:', value:res?.link});
channel.send({embeds:[embed]});
}
}
async checkForInvites(message)
{
let content = message.content;
let guild = message.guildId;
if(!content?.includes('discord.gg')) return
await GuildM.find({gID:guild})
.then((g, err)=>
{
if(err) return;
if(!g.allowInvites)
{
message.delete();
}
return;
});
}
async music()
{
const ServersWithMusicOn = await this.serversWithMusicOn();
const ISGONNABEOFFFORAWHILE = true
if(!ServersWithMusicOn && !ISGONNABEOFFFORAWHILE) return
for(var server of ServersWithMusicOn)
{
if(!Childs[server.name])
{
console.log('Index: Starting Music Worker:', server.name,`(${server.gID})`)
Childs[server.name] = Spawner.fork('./musicWorker.js',[server.gID, server.name]);
}
else
{ //Health check
(function (server)
{
Childs[server.name].on('exit', ()=>
{
console.log('Child DIED, spawning another.')
Childs[server.name] = Spawner.fork('./musicWorker.js',[server.gID, server.name]);
})
})(server);
}
}
}
async serversWithMusicOn()
{
return await GuildM.find({music:true}).then(g=>{return g})
}
}module.exports.Client = _Client;
const ErrorType =
{
Permissions: "Permissions",
Arguments: "Arguments",
NoArguments: "No Arguments Given",
NotOnTheList: "Unknown Command",
OldMessages: "Can't delete this messages.",
FeatureInnactive: "This feature is not active."
}
module.exports.ErrorType = ErrorType;
class Channel
{
/**
*
* @param {String} channelID
*/
constructor(channelID)
{
this.channel = channelID;
}
}
module.exports.Channel = Channel;
/**
*
* @param {Number} length
* @returns {String}
*/
function Random(length)
{
length?length:length=5;
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}module.exports.Random = Random;
class ErrorMessage
{
/**
* @param {_Client} client
*/
constructor(client)
{
this.client = client;
}
/**
*
* @param {ErrorType} errorType
* @param {Message} message
* @param {...String} extraMessages
*/
async send(errorType,message, extraMessages)
{
if (!message.channel?.id) return;
const embed = new EmbedBuilder()
.setColor(0x4d0000)
.setAuthor({name:"Rem-chan", iconURL:"https://i.imgur.com/g6FSNhL.png",url:'https://rem.wordfights.com/addtodiscord'})
.setTimestamp()
.setFooter({text:'Rem-chan on ', iconURL:"https://i.imgur.com/g6FSNhL.png"})
.addFields({name: 'Error:', value: 'something'})
.addFields({name: 'Try:', value: 'something'});
switch (errorType) {
case ErrorType.Arguments:
embed.data.fields[0].value =ErrorType.Arguments;
embed.data.fields[1].value = 'Verify the arguments provided.'
break;
case ErrorType.NoArguments:
embed.data.fields[0].value =ErrorType.NoArguments;
embed.data.fields[1].value = 'Provide the required arguments for this command.'
break;
case ErrorType.Permissions:
embed.data.fields[0].value =ErrorType.Permissions;
embed.data.fields[1].value = 'Ask this servers administrator to use the command.'
break;
case ErrorType.NotOnTheList:
embed.data.fields[0].value =ErrorType.NotOnTheList;
embed.data.fields[1].value = 'Use !help for the list of available.'
break;
case ErrorType.OldMessages:
embed.data.fields[0].value =ErrorType.OldMessages;
break;
case ErrorType.FeatureInnactive:
embed.data.fields[0].value =ErrorType.FeatureInnactive;
embed.data.fields[1].value = 'If you are an admin you can activate the function on the dashboard. (rem.wordfights.com)'
break;
default:
break;
}
if(extraMessages)
{
for(var i = 0; i<extraMessages.length; i++)
{
embed.addFields({name:'Tip:', value:extraMessages[i]});
}
}
const randomID = Random();
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(randomID)
.setLabel('Remove this.')
.setStyle(ButtonStyle.Primary)
.setEmoji('❌')
);
const Message = await this.client.channels.cache.get(message.channel.id).send({ephemeral: true, embeds: [embed], components: [row] });
const filter = i => i.customId === randomID;
const collector = message.channel.createMessageComponentCollector({ filter, time: 60000 });
collector.on('collect', async m =>
{
message.delete().then(()=>m.message.delete())
.catch(()=>m.message.delete());
});
collector.on('end', async ()=>
{
Message.edit({ components: [] });
})
}
}module.exports.ErrorMessage = ErrorMessage;
/**
* Anilist Client -> Search Functions
*/
class AnimeInfo
{
/**
*
* @param {String} title
* @param {String} status
* @param {String} url
* @param {String} episodes
* @param {String} trailer
* @param {String} description
* @param {String} coverImage
*/
constructor(title, status, episodes, url , trailer, description, coverImage)
{
this.title = title;
this.status = status;
this.url = url
this.episodes=episodes;
this.trailer = trailer;
this.description = description;
this.coverImage = coverImage;
}
}
class CharInfo
{
/**
*
* @param {String} name
* @param {String} gender
* @param {String} image
* @param {String} url
* @param {String} description
*/
constructor(name, gender, image, description)
{
this.name = name;
this.gender = gender;
this.url = url
this.image=image;
this.description = description;
}
}
class MangaInfo
{
/**
*
* @param {String} title
* @param {String} status
* @param {String} url
* @param {String} description
* @param {String} coverImage
*/
constructor(title, status, description, coverImage)
{
this.title = title;
this.status = status;
this.url = url
this.description = description;
this.coverImage = coverImage;
}
}
class Options
{
/**
*
* @param {String} method
* @param {String} headers
* @param {String} body
*/
constructor(method, headers, body)
{
this.method = method;
this.headers = headers;
this.body = body;
}
}
class AnilistCli {
/**
*
* @param {Options} options
* @returns {Info}
*/
async getInfo(options)
{
return await fetch('https://graphql.Anilist.co', options).then(handleResponse)
.then(handleData)
.catch(handleError);
function handleResponse(response)
{
return response.json().then(function (json)
{
return response.ok ? json : Promise.reject(json);
});
}
function handleData(data)
{
return data
}
function handleError(error) {
return error;
}
}
/**
*
* @param {String} ss - Search Query
* @returns {AnimeInfo}
*/
async searchAnime(ss)
{
var variables = {
ss,
"page": 0,
"perPage": 4
};
var query = `query ($ss: String, $page: Int, $perPage: Int) {
Page(page: $page, perPage: $perPage) {
media(search: $ss, type: ANIME) {
id
title {
romaji
english
}
coverImage {
medium
}
}
}
}
`
var options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query,
variables
})
};
var data = await this.getInfo(options);
data = data.data.Page.media
if(!data[0]) return 'Error'
var aux = [];
for(var anime of data)
{
aux.push({
title:anime.title,
id:anime.id,
coverImage:anime.coverImage.medium
});
}
return aux;
}
async getAnimeInfo(id)
{
var variables = {
id
};
var query = `query ($id: Int) {
Media(id:$id) {
id
idMal
title {
romaji
english
native
}
siteUrl
status
episodes
season
seasonYear
trailer {
site
id
}
description(asHtml:false)
coverImage {
large
}
}
}`;
var options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query,
variables
})
};
var data = await this.getInfo(options);
data = data.data;
return {
title:data.Media.title,
malLink:'https://myanimelist.net/anime/'+data.Media.idMal,
status:data.Media.status,
url:data.Media.siteUrl,
episodes:data.Media.episodes,
trailer:data.Media.trailer,
description:data.Media.description,
coverImage:data.Media.coverImage.large,
season: (data.Media.season?data.Media.season:'') + ' ' + (data.Media.seasonYear?data.Media.seasonYear:'')
}
}
/**
*
* @param {String} ss - Search Query
* @returns {CharInfo}
*/
async searchChar(ss)
{
var variables = {
ss,
"page": 0,
"perPage": 4
};
var query = `query ($ss: String, $page: Int, $perPage: Int) {
Page(page: $page, perPage: $perPage) {
characters(search: $ss, sort: SEARCH_MATCH) {
id
name {
full
native
}
siteUrl
gender
image {
medium
}
}
}
}
`;
var options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query,
variables
})
};
var data = await this.getInfo(options);
var chars = data.data.Page.characters
var aux = [];
if(!chars[0]) return 'Error'
for(var char of chars)
{
aux.push({
id:char.id,
name:char.name,
gender:char.gender,
url:char.siteUrl,
image:char.image.medium
})
}
return aux;
}
async getCharInfo(id)
{
var variables = {
id
};
var query = `query ($id: Int) {
Character(id: $id, ) {
id
name {
full
native
}
gender
age
siteUrl
image {
medium
}
description(asHtml:false)
}
}
`;
var options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query,
variables
})
};
var data = await this.getInfo(options);
data = data.data;
return {
name:data.Character.name,
gender:data.Character.gender,
url:data.Character.siteUrl,
image:data.Character.image.medium,
description:data.Character.description?data.Character.description:' ',
age:data.Character.age,
}
}
/**
*
* @param {String} ss - Search Query
* @returns {MangaInfo}
*/
async searchManga(ss)
{
var variables =
{
ss,
"page": 0,
"perPage": 4
}
var query = `query ($ss: String, $page: Int, $perPage: Int) {
Page(page: $page, perPage: $perPage) {
media(search: $ss, type: MANGA) {
id
title {
romaji
english
}
coverImage {
medium
}
}
}
}`
var options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query,
variables
})
};
var data = await this.getInfo(options);
var mangas = data.data.Page.media;
var aux = [];
if(!mangas[0]) return 'Error'
for(var manga of mangas)
{
aux.push({
id:manga.id,
title:manga.title,
image:manga.coverImage.medium
})
}
return aux;
}
async getMangaInfo(id)
{
var variables = {
id
};
var query = `query ($id: Int) {
Media(id: $id) {
title {
romaji
english
native
}
siteUrl
status
description(asHtml: false)
coverImage {
large
}
}
}
`;
var options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query,
variables
})
};
var data = await this.getInfo(options);
data = data.data;
return {
title:data.Media.title,
status:data.Media.status,
url:data.Media.siteUrl,
description:data.Media.description,
coverImage:data.Media.coverImage.large,
}
}
}module.exports.Anilist = AnilistCli;
const channelM = require('./models/channels');
class rustCommits
{
/**
*
* @param {_Client} client
*/
constructor(client)
{
this.client = client;
this.update();
setInterval(() => {
this.update();
}, 3*60*1000);
this.channels;
}
async update ()
{
const channels = await channelM.find({for:'rust'}).then(channels=>{return channels});
this.channels = await this.resolveChannels(channels);
const Res = await fetch("https://commits.facepunch.com/?format=json").then(handleResponse)
.then(handleData)
.catch(handleError);
function handleResponse(response)
{
return response.json().then(function (json)
{
return response.ok ? json : Promise.reject(json);
});
}
function handleError(error) {
return error;
}
function handleData(data) {
data.results.splice(30, 20);
return data.results.filter(x=>x.repo.search(/rust/i)!=-1)
}
if(!Res.length)
{
//console.log('Framework: Rustcommits: Res', Res);
return this.update();
}
for(var j = 0; j<this.channels.length; j++)
{
if(!this.channels[j].lastid) this.channels[j].lastid = 0;
const Pos = Res.map(e => e.id).indexOf(this.channels[j].lastid);
Res.splice(Pos, Res.length-Pos);
for(var i = Res.length-1; i>0; i--)
{
if(Res[i].id > this.channels[j].lastid)
{
send(
{
Author:Res[i].user.name,
Avatar:Res[i].user.avatar,
Time:Res[i].created.split("T")[1]+ " of "+ Res[i].created.split("T")[0],
Content:Res[i].message,
ID:Res[i].id,
Repo:Res[i].repo,
Branch:Res[i].branch
}, this.channels[j].cID, this.client);
}
}
}
/**
*
* @param {RustCommit} commit
* @param {Channel} channel
*/
function send(commit, channel, client)
{
client.channels.cache.get(channel).send({embeds:[
new EmbedBuilder()
.setColor(0xc23811)
.setTitle(commit.Time)
.setURL('https://commits.facepunch.com')
.setAuthor({name:commit.Author, iconURL:commit.Avatar==''?"https://i.imgur.com/g6FSNhL.png":commit.Avatar,url:`https://commits.facepunch.com/${commit.Author.split(' ').join('')}`})
.setDescription(commit.Content)
.addFields(
{ name:`${commit.Repo}`, value:`[${commit.Repo}/${commit.Branch}](https://commits.facepunch.com/r/${commit.Repo}/${commit.Branch.split(' ')? commit.Branch.split(' ').join('%20'):commit.Branch} 'Branch Link')`},
{ name: 'ID', value: commit.ID.toString() },
)
.setTimestamp()
.setFooter({text:'Rem-chan on ', iconURL:"https://i.imgur.com/g6FSNhL.png"})
]});
}
}
/**
*
* @param {channelM} channels
*/
async resolveChannels(channels)
{
for await(var channel of channels )
{
if(this.client.channels.cache.get(channel.cID))
{
const Channel = this.client.channels.cache.get(channel.cID)
const lastid = await Channel.messages.fetch({limit:1}).then(message=>
{
if(!message) return 0
const [content] = message.values();
if(!content||!content.embeds||!content.embeds.length>0) return 0;
return parseInt(content.embeds[0].fields[1].value)
})
channel['lastid'] = lastid;
}
}
return channels;
}
}
class RustCommit
{
/**
*
* @param {String} Author
* @param {String} Avatar - IMAGE URL
* @param {String} Time
* @param {String} Content
* @param {Number} ID
* @param {String} Repo
* @param {String} Branch
*/
constructor(Author, Avatar, Time, Content, ID, Repo, Branch)
{
this.Author = Author
this.Avatar = Avatar
this.Time = Time
this.Content = Content
this.ID = ID
this.Repo = Repo
this.Branch = Branch
}
}
class FreeGames
{
/**
*
* @param {_Client} client
*/
constructor(client)
{
this.client = client;
this.update()
setTimeout(() => {
this.update();
}, 3*60*1000);
}
async update()
{
const channels = await channelM.find({for:'freegames'}).then(channels=>{return channels});
this.channels = await this.resolveChannels(channels);
const Res = await fetch('https://www.gamerpower.com/api/giveaways').then(handleResponse)
.then(handleData)
.catch(handleError);
function handleResponse(response)
{
return response.json().then(function (json)
{
return response.ok ? json : Promise.reject(json);
});
}
function handleError(error) {
return error;
}
function handleData(data) {
data.splice(10, data.length-10);
return data
}
//if(!Res.length) console.log('FrameWork -> FreeGames: Res:',Res);
if(!Res.length) return setTimeout(() =>
{
this.update();
}, 60*1000);
for(var j = 0; j<this.channels.length; j++)
{
const Pos = Res.map(e => e.id).indexOf(this.channels[j].lastid);
Res.splice(Pos, Res.length-Pos)
for(var i = Res.length-1; i>0; i--)
{
if(Res[i].id > this.channels[j].lastid)
{
send(
{
ID:Res[i].id, //
Title:Res[i].title, //
Type:Res[i].type, //
Thumb:Res[i].thumbnail, //
Image:Res[i].image, //
Description:Res[i].description, //
Instructions:Res[i].instructions,//
URL:Res[i].open_giveaway_url,//
Platform:Res[i].platforms,//
EndDate:Res[i].end_date,
}, this.channels[j].cID, this.client);
}
}
}
/**
*
* @param {FreeGameModel} game
* @param {Channel} channel
*/
function send(game, channel, client)
{
client.channels.cache.get(channel).send({embeds:[
new EmbedBuilder()
.setColor(0x2d9134)
.setURL(game.URL)
.setTitle(game.Title)
.setAuthor({name:"Rem-chan", iconURL:"https://i.imgur.com/g6FSNhL.png",url:'https://rem.wordfights.com/addtodiscord'})
.setDescription(game.Description)
.setImage(game.Image)
.setThumbnail(game.Thumb)
.addFields(
{ name: 'Platforms', value: game.Platform},
{ name: 'Type of offer:', value: game.Type},
{ name:'Instructions:', value:game.Instructions},
{ name:`End date:`, value:`${game.EndDate}`},
)
.setTimestamp()
.setFooter({text:`(${game.ID}) Rem-chan on `, iconURL:"https://i.imgur.com/g6FSNhL.png"})
]});
}
}
/**
*
* @param {channelM} channels
*/
async resolveChannels(channels)
{
for await(var channel of channels )
{
if(this.client.channels.cache.get(channel.cID))
{
const Channel = this.client.channels.cache.get(channel.cID)
const lastid = await Channel.messages.fetch({limit:1}).then(message=>
{
if(!message) return 0
const [content] = message.values();
if(!content||!content.embeds||!content.embeds.length>0) return 0;
return parseInt(content.embeds[0].footer.text.split('(')[1].split(')')[0])
})
channel.lastid = lastid;
}
}
return channels;
}
}
class FreeGameModel
{
/**
* @param {String} ID
* @param {String} Title
* @param {String} Thumb
* @param {String} Type
* @param {String} Image
* @param {String} Description
* @param {String} Instructions
* @param {String} URL
* @param {String} Platform
* @param {Date} EndDate
*/
constructor(ID, Title, Thumb,Type, Image, Description, Instructions, URL, Platform, EndDate)
{
this.ID = ID;
this.Title = Title;
this.Type = Type;
this.Thumb = Thumb;
this.Image = Image;
this.Description = Description;
this.Instructions = Instructions;
this.URL = URL;
this.Platform = Platform;
this.EndDate = EndDate;
}
}
const strikesM = require('./models/strikes');
const guildsM = require('./models/guilds');
class Strikes
{
constructor(client)
{
this.client = client;
this.check();
setInterval(() => {
this.check();
}, 5*60*1000);
}
async getAreStrikesActive(guildId)
{
const guild = await guildsM.find({gID:guildId}).then(g=>{return g[0]});
return guild.strikes;
}
async check()
{
/**
* TODO: Grab validated strikes.
* TODO: Check if guild has strikes active.
* TODO: Handle player if strikes have reached the threshold
*
*/
const Strikes = await strikesM.find().then(s=>{return s})
if(Strikes.length==0) return console.log('Lib: Striker: No Strikes')
const guilds = await guildsM.find({strikes:true}).then(g=>{return g})
.then(guilds => guilds.map(guild => guild.gID));;
if(guilds.length==0) return console.log('Lib: Striker: No Guilds')
const validStrikes = await strikesM.find({validated:true}).then(res=>{return res});
const strikesByGuildID = new Map();
validStrikes.forEach(strike =>
{
const { strokedID, guildID } = strike;
// If the guildID key doesn't exist in strikesByGuildID, create it
if (!strikesByGuildID.has(guildID)) strikesByGuildID.set(guildID, new Map());
const strikesByStrokedID = strikesByGuildID.get(guildID);
// If the strokedID key doesn't exist in the strikesByStrokedID map, create it
if (!strikesByStrokedID.has(strokedID)) strikesByStrokedID.set(strokedID, [strike]);
else strikesByStrokedID.get(strokedID).push(strike);
});
const guildsWith3Strikes = new Map();
strikesByGuildID.forEach((strikesByStrokedID, guildID) => {
// Filter strikes by each strokedID to find users with at least three strikes
const usersWith3Strikes = [];
strikesByStrokedID.forEach((strikes, strokedID) => {
if (strikes.length >= 3) {
usersWith3Strikes.push({ user: strokedID, strikes });
}
});
// If there are users with at least three strikes, add them to the map
if (usersWith3Strikes.length > 0) {
guildsWith3Strikes.set(guildID, usersWith3Strikes);
}
});
const guildsWith3StrikesFiltered = new Map([...guildsWith3Strikes].filter(([guildID]) => guilds.includes(guildID)));
guildsWith3StrikesFiltered.forEach((guild, guildid)=>
{
guild.forEach((user)=>
{
this.handleStroked(user,guildid);
})
})
}
handleStroked(user, guild)
{
const member = this.client.guilds.cache.get(guild).members.cache.get(user.user);
if (!member) return console.log('Lib:Strikes: Handle Struck: Member not valid', member)
const reason = `Strike 1: ${user.strikes[0].reason}; Strike 2: ${user.strikes[1].reason}; Strike 3: ${user.strikes[2].reason}`;
member.kick(reason)
.then(() => console.log(`Successfully kicked ${member.user.username}`))
.catch(err => console.log(`Unable to kick ${member.user.username}: ${err}`));
strikesM.deleteMany({ guildID: guild, strokedID: user.user }, (err, res) => {
if (!err) return console.log('Lib: Strikes: Deleted: From User:', member.user.username, 'on Guild:', member.guild.name)
});
}
}
module.exports.Strikes = Strikes;
exports.DiscordAPI = class DiscordAPI
{
constructor (cookie, bot)
{
this.authorization = this.AuthStringMaker(cookie);
this.bot = bot;
this.userId = '';
}
async getUser ()
{
return await fetch('https://discord.com/api/users/@me',
{
headers: {
authorization: this.authorization,
},
})
.then(result=>result.json())
.then(response =>
{
return response;
})
}
async getUserId()
{
return this.userId = await this.getUser().then(user=>{return user.id});
}
async getUserGuilds()
{
return await fetch('https://discord.com/api/users/@me/guilds',
{
headers: {
authorization: this.authorization,
},
})
.then(result => result.json())
.then(response =>
{
if(response.message) return response.message
return response.filter(x=>x.owner==true);
})
}
async getGuildsFull()
{
var userGuilds = await this.getUserGuilds()
var promises = [];
for(var guild of userGuilds) {
promises.push(new Promise((resolve, reject) => {
(async (guild)=>
{
var aux = await this.getSpecificGuildData(guild.id);
var roleRules = aux ? await this.getRoleRules(guild.id):null;
var guildTextChannels = aux ? aux.TextChannels:null;
var emojis = aux ? aux.emojis:null;
var roles = aux ? aux.roles:null;
const dataObject =
{
id:guild.id,
music:aux.guild?aux.guild.music:false,
strikes:aux.guild?aux.guild.strikes:false,
allowInvites:aux?.guild?.allowInvites,
name:guild.name,
icon:guild.icon?`https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.webp`:'https://cdn3.iconfinder.com/data/icons/popular-services-brands-vol-2/512/discord-512.png',
features:guild.features,
hasRem:aux.extra?true:false,
memberCount:aux.extra?aux.extra.memberCount:null,
roleRules,
guildTextChannels,
emojis,
roles
};
resolve(dataObject);
})(guild)
}));
}
const guilds = await Promise.all(promises).then((values) =>
{
return values
});
return guilds;
}
async getSpecificGuildData(guildID)
{
var extra = this.bot.guilds.cache.get(guildID)
var guild = await GuildM.find({gID:guildID})
.then((guild,err)=>
{
if(err || guild.length==0) return;
return {guild};
});
var TextChannels = [];
var emojis = [];
var roles = [];
if(extra)
{
for(var channel of extra.channels.cache)
{
if(channel[1].type==0)
{
TextChannels.push(
{
'id':channel[0],
'name':channel[1].name
});
}
}
for(var emoji of extra.emojis.cache)
{
emojis.push(
{
'id':emoji[0],
'name':emoji[1].name
});
}
for(var role of extra.roles.cache)
{
roles.push(
{
'id':role[0],
'name':role[1].name
});
}
}
try{
guild= guild.guild[0];
}
catch (err)
{
console.log('There was an error fetching the guild', err)
}
return {guild, extra, TextChannels, emojis, roles};
}
async getRoleRules(guildid)
{
return await roleRulesM.find({gID:guildid}).then((rules, err)=>
{
if(err || rules.length==0) return
return rules
})
}
AuthStringMaker(cookieString)
{
const splitCookie = cookieString.split('; ');
const tokenType = splitCookie[1].split('=')[1];
const accessToken = splitCookie[0].split('=')[1];
return `${tokenType} ${accessToken}`
}
/**
*
* @param {String} id GuildID
* @returns
*/
async getSpecificGuildDataForDash(id)
{
const aux = await this.getSpecificGuildData(id);
const roleRules = aux ? await this.getRoleRules(id):null;
const guildTextChannels = aux ? aux.TextChannels:null;
const emojis = aux ? aux.emojis:null;
const roles = aux ? aux.roles:null;
return {
id,
music:aux.guild?aux.guild.music:false,
strikes:aux.guild?aux.guild.strikes:false,
allowInvites:aux?.guild?.allowInvites,
name:aux.guild.name,
icon:aux.icon?`https://cdn.discordapp.com/icons/${aux.id}/${aux.icon}.webp`:'https://cdn3.iconfinder.com/data/icons/popular-services-brands-vol-2/512/discord-512.png',
hasRem:aux.extra?true:false,
memberCount:aux.extra?aux.extra.memberCount:null,
roleRules,
guildTextChannels,
emojis,
roles
};
}
}
/**
*
* @param {String|String} id - Discord Guild Id (GuildId) | Mongo Record ID (ruleID)
* @returns {Array<Object>|String} payload
*/
exports.getRules = async function getRules(id)
{
if(!id) return 'Error: No Guild or Rule Id '
if(mongoose.Types.ObjectId.isValid(id)) return await roleRulesM.find({_id:id}).then(rule=>{return rule[0]})
return await roleRulesM.find({gID:id})
.then(rules =>{return rules})
.catch(()=>{return null})
}
/**
* @param {Array.<string, string,string>} args - [Discord Guildid,Discord channelId, YT channelURL]
* @param {_Client} bot
* @returns
*/
exports.confirmArgsForYTFeed = async function confirmArgs(args, bot)
{
var channelId = args[1];
var channelURL = args[2];
async function getChannelId(url)
{
if(url.split('@').length<1) return
return await fetch(url)
.then(handleResponse)
.then(handleData)
.catch(handleError);
function handleResponse(response)
{
if(response.ok) return response.text();
}
function handleError(error)
{
return {error:'Bad youtube channel link.'};
}
function handleData(data)
{
return data ? data.split('https://www.youtube.com/feeds/videos.xml?channel_id=')[1].split('"')[0] : false;
}
}
const YTChannelId = await getChannelId(channelURL);
if(YTChannelId.error) return {error:YTChannelId.error}
const discordChannel = await bot.channels.fetch(channelId).then(channel=>{return channel }).catch(()=>{return null});
if(!discordChannel) return {error:'Provided discord channel id does not exist.'}
var name = await fetch(`https://www.youtube.com/feeds/videos.xml?channel_id=${YTChannelId}`)
.then(response =>
{
return response.text()
})
.then(data=>
{
data = xmlparser.xml2json(data,
{
compact: true,
space: 4
});
data = JSON.parse(data);
return data.feed.author.name._text
})
.catch(error=>{return error;});
return {YTChannelId, channelId, channelName:name}
}