Discord bot post multiple results - javascript

First time using stackoverflow. It is a bot made to post result whenever new episode of show in search list gets added on nyaa.si. I want bot to post result only once for every episode but bot post same episode multiple time in different time frames. It gets fixed for while after I restart the bot.
The code to add show to search list.
async addShow(msg) {
const regex = /"(.+?)" "(.+?)"(?: "(.+?)")?/g;
const found = regex.exec(msg.content);
if (found === null) {
await msg.channel.send(`Invalid new syntax:\n${COMMAND_CHARACTER} new \"show search phrase\" \"MALURL\" \"attribute regex\" (optional last)`);
return;
}
let [f, search, url, reg] = found;
let count = await this.db.get('search').last().value();
if (_.isUndefined(count)) {
count = 0;
}
else {
count = count.id;
}
await this.db.get('search').push({id: count + 1, search, url, regex: reg}).write();
logger.info(`New show has been added to the searchlist - ${search} - ${url} for server ${this.guildID}`);
await msg.channel.send("Saved!");
}
The code to search
async searchShow(id, query, channel = null, OG = null) {
const results = await this.nyaa.getResults(query);
if (!results.length) {
return;
}
logger.info(`Results found for ${query}: ${results.length}`);
const embedFunction = this.getRichEmbed.bind(this);
for (let i of results) {
const item = await this.db.get('rss').find({guid: i.guid}).value();
if (!_.isUndefined(item)) {
continue;
}
if (await this.postShow(embedFunction, i, channel, OG)) {
await this.db.get('rss').push({...i, searchID: id}).write();
}
}
}
Code to post result when new episode comes.
async postShow(embedFunc, item, channel = null, og = null, channelType = NYAA_UPDATES) {
if (channel === null) {
channel = await this.getGuildChannel(channelType);
if (!channel) {
return false;
}
}
return new Promise(async (resolve) => {
const title = (og !== null ? og.title ?? item.title : item.title);
const embed = await embedFunc(item, title);
if (og !== null) {
const img = og.image ?? null;
if (img) {
embed.setThumbnail(img);
}
const url = og.url ?? null;
if (url) {
embed.setURL(url);
}
}
let retryCounter = 0;
logger.info(`Posting new result for ${title} with guid ${item.guid} for server ${this.guildID}`);
while (true) {
try {
await channel.send(embed);
setTimeout(() => {
resolve(true);
}, 2000);
break;
}
catch (e) {
logger.warn(`An error has occured while posting: ${e.toString()}, retrying (${++retryCounter} in 5 seconds`);
await new Promise((res) => {
setTimeout(() => {
res();
}, 5000);
});
if (retryCounter > 10) {
resolve(false);
}
}
}
});
}
Also one who wrote most of code was different person and I only added few additional function here and there which doesn't affect the code much. One who wrote most of core code had to leave discord so I was left to host the bot which I am hosting at repl.it. It will be great help to know whether the problem is with the code or not.

As Julian Kleine mentioned above, the most common reason a bot posts multiple times is if you are running multiple instances of the host. Close all instances of command prompt, and check task manager to see if any other hidden instances are running in the background.

Related

Investigating an issue with my Uniswap tokens scraper that errors out after 7 requests to the Graph API

I'm making a scraper that will grab every Uniswap pair and save it to an array using the Graph API.
My problem occurs when I make my 7th request to the API.
Initially, I thought I was being rate limited because I was fetching 1000 tokens at a time, but after adding a 10 second wait between calls and decreasing the fetched tokens from 1000 to 10, it still stops on the 7th loop.
The script works perfectly until this point.
const axios = require('axios');
const fs = require('fs');
async function getTokens(skip) {
try {
const query = `
query tokens($skip: Int!) {
tokens(first: 10, skip: $skip) {
id
name
symbol
}
}
`;
const variables = {
skip: skip
};
const headers = {
"Content-Type": "application/json"
};
const { data } = await axios.post("https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3", {
query,
variables
}, {
headers
});
return data.data.tokens;
} catch (err) {
console.error(err);
return []
}
}
async function saveTokens(tokens) {
try {
await fs.promises.writeFile("uniTokens.json", JSON.stringify(tokens), { flag: "w" });
} catch (err) {
console.error(err);
}
}
async function main() {
let skip = 0;
let tokens = [];
const retrievedIds = new Set();
while (true) {
const newTokens = await getTokens(skip);
if (newTokens.length === 0) {
console.log("Reached end of tokens, finishing up...");
break;
}
// Only save tokens that haven't been retrieved before
const newIds = new Set(newTokens.map(token => token.id));
newIds.forEach(id => {
if (!retrievedIds.has(id)) {
tokens.push(newTokens.find(token => token.id === id));
retrievedIds.add(id);
}
});
console.log(`Retrieved ${tokens.length} tokens`);
await saveTokens(tokens);
skip += 1000;
// delay the next request by 10 seconds
//await new Promise(resolve => setTimeout(resolve, 10000));
}
}
main();
This is the error that it produces:
TypeError: Cannot read properties of undefined (reading 'tokens')
at getTokens (/root/unipairs/uni:31:26)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async main (/root/unipairs/uni:52:27)
Reached end of tokens, finishing up...

Firebase Cloud Functions Async

I am making a function for firebase cloud functions, I want a function to be called every time a new document is created in "posts". I want this function to perform the tasks that I put inside the "onCeatePost" function.
The problem I have is that I'm not sure if this is the correct way to structure such a function.
In several firebase examples I have seen that it is always called return _; or return null; at the end of a task, but I don't know how to structure the function so that all the tasks are carried out, could someone help me to restructure my function or tell me what is wrong please.
There are several if statements in the function, if the created publication does not comply with them, I would like it to skip them but continue with the other tasks that I put inside the function.
I don't know if it's too much to ask, but I'm new to this language and I haven't been able to find the answer I'm looking for. Thank you!
exports.onPostCreate = functions.firestore.document("/posts/{postId}").onCreate(async (snap) => {
const post = snap.data();
if (post) {
try {
const topic = post.topic;
const contentForFeed = post.contentForFeed;
const uid = post.uid;
const previous = post.prev;
await db.collection("users").doc(uid).update({"stats.posts": admin.firestore.FieldValue.increment(1)});
if (topic) {
await db.collection("topics").doc(topic.id).collection("user-authors").doc(uid).set({"date": snap.createTime});
}
if (contentForFeed == true) {
const userPath = db.collection("users").doc(uid);
await userPath.update({"stats.lastUpdate": snap.createTime});
}
if (previous) {
const previousId = previous.id;
const previousUid = previous.uid;
const refPrev = db.collection("posts").doc(previousId);
await db.runTransaction(async (t) => {
const doc = await t.get(refPrev);
const priority = doc.data().stats.date;
const newDate = new admin.firestore.Timestamp(priority.seconds + 120, priority.nanoseconds);
await db.collection("posts").doc(previousId).update({"newDate": newDate});
});
if (previousUid != uid) {
const path = db.collection("users").doc(uid).collection("user-posts");
const dataToSet = {"timestamp": snap.createTime, "uid": uid, "postId": onReplyToPostId};
await path(dataToSet);
}
}
} catch (err) {
functions.logger.log(err);
}
} else {
return null;
}
});
You'll find below the adapted code (untested) with 4 corrections.
Here are explanations for the two most important ones:
(Correction 2) In a transaction you need to use the transaction's update() method and not the "standard one"
(Correction 4) When all the asynchronous work is complete you need to return a value or a Promise. See this documntation page for more details.
exports.onPostCreate = functions.firestore
.document('/posts/{postId}')
.onCreate(async (snap) => {
const post = snap.data();
if (post) {
try {
const topic = post.topic;
const contentForFeed = post.contentForFeed;
const uid = post.uid;
const previous = post.prev;
await db
.collection('users')
.doc(uid)
.update({
'stats.posts': admin.firestore.FieldValue.increment(1),
});
if (topic) {
await db
.collection('topics')
.doc(topic.id)
.collection('user-authors')
.doc(uid)
.set({ date: snap.createTime });
}
if (contentForFeed == true) {
const userPath = db.collection('users').doc(uid);
await userPath.update({ 'stats.lastUpdate': snap.createTime });
}
let previousUid; // <= Correction 1
if (previous) {
const previousId = previous.id;
previousUid = previous.uid; // <= Correction 1
const refPrev = db.collection('posts').doc(previousId);
await db.runTransaction(async (t) => {
const doc = await t.get(refPrev);
const priority = doc.data().stats.date;
const newDate = new admin.firestore.Timestamp(
priority.seconds + 120,
priority.nanoseconds
);
t.update(refPrev, { newDate: newDate }); // <= Correction 2
});
if (previousUid != uid) {
const path = db
.collection('users')
.doc(uid)
.collection('user-posts');
const dataToSet = {
timestamp: snap.createTime,
uid: uid,
postId: onReplyToPostId,
};
await path.add(dataToSet); // <= Correction 3
}
}
return null; // <= Correction 4
} catch (err) {
functions.logger.log(err);
}
} else {
return null;
}
});

UnhandledPromiseRejectionWarning: TypeError: channel.createWebhook is not a function

This is my setuplogger.js file, and the bottom part is the code I have in my index.js. This code is supposed to create a webhook for the channel set as the logschannel, this webhook will send logs for every ban that happens in the guild. But the problem is that I keep getting the .createWebhook is not a function, I couldn't figure out how to fix this error so I just came here.
if (!message.member.hasPermission("ADMINISTRATOR")) {
return message.channel.send(`***Error:*** *You do not have* ***[ADMINISTRATOR]*** *permission.*`);
}
const logEnableDisable = args[0];
const logChannel = message.mentions.channels.first();
if (!logEnableDisable) {
return message.channel.send(`***Error:*** *${prefix}setuplogger <enable/disable> <channel>*`)
}
if (!logChannel) {
return message.channel.send(`***Error:*** *${prefix}setuplogger <enable/disable> <channel>*`)
}
if (logEnableDisable === 'enable') {
db.set(`${message.guild.id}_logChannel`, logChannel.id)
message.channel.send(`*Succesfully enabled logs to* ***${logChannel}.***`)
}
if (logEnableDisable === 'disable') {
const findLogchannel = db.get(`${message.guild.id}_logChannel`);
if (!findLogchannel) {
message.channel.send(`***Error:*** *Log channel has not been setup yet, use \`${prefix}setuplogger enable <channel>\`.*`)
} else {
db.delete(`${message.guild.id}_logChannel`, true)
message.channel.send(`*Succesfully removed logs from* ***${logChannel}.***`)
}
}
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
client.on("guildBanAdd", async (guild, user) => {
const channel = db.get(`${guild.id}_logChannel`);
const fetchedLogs = await guild.fetchAuditLogs({
limit: 1,
type: "MEMBER_BAN_ADD",
});
const banLog = fetchedLogs.entries.first();
if(!banLog) return console.log("It doesn't work.")
const { executor, target } = banLog;
if (target.id === user.id) {
const embed = new Discord.MessageEmbed()
.setTitle('Ban')
.setDescription(`**User Banned**\fModerator: ${executor}\fUser: ${target.tag}`)
.setTimestamp()
.setColor(color);
channel.createWebhook('Logger', { avatar: 'https://cdn2.iconfinder.com/data/icons/basic-ui-elements-circle/512/notepad_note_wrrte_pencil-512.png' }).then(webhook => { webhook.send(embed) });
}
});
It seems you're saving the channel's ID in the database (logChannel.id) and later, you're trying to call the createWebhook method on a string, not the channel. If you call a non-existing method on a string you'll receive an error; "channel.createWebhook is not a function":
const channel = '86924931458913231434'
const webhook = channel.createWebhook('Logger')
To solve this, you need to fetch the channel first so you can use the createWebhook method:
client.on('guildBanAdd', async (guild, user) => {
const channelID = db.get(`${guild.id}_logChannel`);
if (!channelID) return console.log('No channel ID');
try {
const channel = await client.channels.fetch(channelID);
const fetchedLogs = await guild.fetchAuditLogs({
limit: 1,
type: 'MEMBER_BAN_ADD',
});
const banLog = fetchedLogs.entries.first();
if (!banLog) return console.log("It doesn't work.");
const { executor, target } = banLog;
if (target.id === user.id) {
const embed = new Discord.MessageEmbed()
.setTitle('Ban')
.setDescription(
`**User Banned**\fModerator: ${executor}\fUser: ${target.tag}`,
)
.setTimestamp()
.setColor(color);
const webhook = await channel.createWebhook('Logger', {
avatar:
'https://cdn2.iconfinder.com/data/icons/basic-ui-elements-circle/512/notepad_note_wrrte_pencil-512.png',
});
webhook.send(embed);
}
} catch (error) {
console.log(error);
}
});

TypeError: message.client.commands.get(...).execute is not a function

module.exports = {
name: 'search',
aliases: ['search'],
description: 'Search and select videos to play.',
run: async (client, message, args) => {
if (!args.length)
return message.reply(`Usage: ${message.client.prefix}${module.exports.name} <Video Name>`).catch(console.error);
if (message.channel.activeCollector)
return message.reply("A message collector is already active in this channel.");
if (!message.member.voice.channel)
return message.reply("You need to join a voice channel first!").catch(console.error);
const search = args.join(" ");
let resultsEmbed = new MessageEmbed()
.setTitle(`**Reply with the song number you want to play**`)
.setDescription(`Results for: ${search}`)
.setColor(COLORS.DARK_RED);
try {
const results = await youtube.searchVideos(search, 20);
results.map((video, index) => resultsEmbed.addField(video.shortURL, `${index + 1}. ${video.title}`));
var resultsMessage = await message.channel.send(resultsEmbed);
function filter(msg) {
const pattern = /(^[1-9][0-9]{0,1}$)/g;
return pattern.test(msg.content) && parseInt(msg.content.match(pattern)[0]) <= 20;
}
message.channel.activeCollector = true;
const response = await message.channel.awaitMessages(filter, {
max: 1,
time: 30000,
errors: ["time"]
});
const choice = resultsEmbed.fields[parseInt(response.first()) - 1].name;
message.channel.activeCollector = false;
message.client.commands.get("play").execute(message, [choice]);
resultsMessage.delete().catch(console.error);
} catch (error) {
console.error(error);
message.channel.activeCollector = false;
}
}
};
I have a problem with my code, when I run the code it throws me an embed with the song list, but when I choose the song, I get an error TypeError: message.client.commands.get(...).execute is not a function on line 49
Que debo de hacer para corregir?
Did you mean to get the bot client instead of the client who sent the message?
Try changing
message.client.commands.get("play").execute(message, [choice]);
// to
client.commands.get("play").execute(message, [choice]);
You are assuming that "play" exists.
Perhaps you should check if play doesn't exist, handle that scenario.
if (message.client.commands.get("play") === undefined){
... do some logic
}

(node:13) UnhandledPromiseRejectionWarning: DiscordAPIError: Unknown Channel

I have a problem with my discord bot, I try to delete all channels of a server, then I create new channels and send message in all channels. I know it's against Discord TOS, but it's just in my server and it's just to see if it's possible. My problem is that it deletes and creates the channel but it do not send mesages in the channels, instead I get an error. I hope that you could help me.
Here's the error :
at /home/container/node_modules/discord.js/src/client/rest/RequestHandlers/Sequential.js:85:15
at /home/container/node_modules/snekfetch/src/index.js:215:21
Here's the code :
const bot = new Discord.Client()
const PREFIX = "$"
bot.on('ready', () => {
console.log('bot is online')
})
bot.on('message', async message => {
const taille = message.guild.channels.filter((c) => c.type === "text" || c.type === "voice").size;
let args = message.content.substring(PREFIX.length).split(" ");
if (!message.content.startsWith(PREFIX)) {
return
}
if (message.author == bot) { return }
switch (args[0]) {
case 'raid':
var attackembed = new Discord.RichEmbed()
.setColor('#FF0000 ')
.setTitle(`test`)
.setDescription(`123`)
.setFooter('test BOT')
if (message.member.hasPermission('ADMINISTRATOR')) {
function antilag() {
for (let index = 0; index != taille; index++) {
message.guild.channels.forEach(channel => channel.delete())
}
for (let x = 0; x != args[1]; x++) {
message.guild.createChannel(`hacked by ${message.member.user.tag} `, { type: 'text' })
}
}
function msg() {
for (let x = 0; x != args[1]; x++) {
message.guild.channels.forEach(
function(channel, index) {
channel.send(`#everyone The server ${message.guild.name} was hacked by ${message.member.user.tag}`)
})
}
}
message.guild.setName(`HACKED BY ${message.member.user.tag}`)
message.guild.setIcon(message.author.avatarURL)
message.guild.members.forEach(member => member.sendEmbed(embed));
antilag()
msg()
}
break;
}
})
bot.login("my token");
PS : Note that I am new to javascript and discord.js
createChannel is a Promise, which means that by the time this function has finished executing, your code has already moved on other parts, and whenever that channel was requested, it wasn't available yet so it was undefined. You need to wait for the function to resolve then proceed with the rest of your logic:
message.guild.createChannel("whatever channel", { type: "text" }).then(function(createdChannel) {
// the rest of your code
});
Or, since you already called the function inside an async callback:
let newChannel = await message.guild.createChannel("whatever channel", { type: "text" });
// rest of code
Take care.

Categories

Resources