I am trying to make a bot with Eris and I need it to be able to call people in direct messaging. This is what I came up with, but clearly, I'm missing something as it does not work.
if (contacts[name].dm) {
const channel = await bot.getDMChannel(contacts[name].id)
await channel.ring([contacts[name].id])
}else{
bot.joinVoiceChannel(contacts[name].id)
}
Might not be the most effective or clean way to do it, but I managed.
function discordAPI(authToken,apiEndpoint,JSONparams,type="GET") {
var xhr=new XMLHttpRequest()
console.log("Connecting to https://discord.com/api/v9"+apiEndpoint)
xhr.open(type,"https://discord.com/api/v9"+apiEndpoint,true)
xhr.setRequestHeader('Content-Type','application/json')
xhr.setRequestHeader('Authorization',authToken)
console.log(authToken)
xhr.onload = function () {
console.log('DONE: ', xhr.status);
console.log('REPONSE: ', xhr.reponseText);
// console.log('=======================');
// console.log('REPONSE_HEADERS:\n', xhr.getAllResponseHeaders());
// console.log('=======================')
};
xhr.send(JSON.stringify(JSONparams))
}
//...
const channel = await bot.getDMChannel(id)
await sleep(time*1000)
discordAPI(bot.token, ('/channels/' + channel.id + '/call/ring'), '', 'POST')
bot.joinVoiceChannel(channel.id).then((vconnect) => { //THIS IS NECESSARY TO JOIN THE CALL!!
console.log(vconnect)
if(sf!='null'){vconnect.play(__dirname + "/../sfx/" + sf + ".mp3")}
vconnect.once("userDisconnect", () => { if(userID == id){bot.switchChannel(null)} });
vconnect.once("end", () => { bot.switchChannel(null) });
vconnect.once("error", () => { console.log("----\n"+Error.toString()+"----\n") });
})
await sleep(timespent*60*1000)
bot.disconnect()
console.log('Called.')
bot.connect()
Related
I'm a non-professional using nodejs server (backend) and javascript/html (frontend) to fetch data from two API's: one API gives a response and I use an ID from the first API to fetch data from the other API. The API returns XML so I use XML2Json and JSON.parse to get the Javascript Object.
everything works perfect until I get to the "return nestedFunction(new_details")-function in the second API-call
so this is where the results are sent back to the client
I do it for the first API and it works fine (backend + frontend)
I tried Async/await but the problem isn't solved
I get the error: "UnhandledPromiseRejectionWarning: TypeError: nestedFunction is not a function"
What could be the problem?
app.get('/AATGetTermMatch', function(req,res) {
let termMatch = req.query.term;
let termLogop = req.query.logop;
let termNotes = req.query.notes;
AATGetTermMatch(termMatch, termLogop, termNotes, function (conceptResults) {
res.send(conceptResults);
});
});
function AATGetTermMatch(termMatch, termLogop, termNotes, nestedFunction) {
let URL = baseURL + "AATGetTermMatch?term=" + termMatch + "&logop=" + termLogop + "¬es=" + termNotes;
fetch(URL)
.then(function (response){
return response.text();
}).then(function (response) {
APICallResults = response;
parseJson();
let objectAPI = JSON.parse(APICallResults);
let full_Concepts = objectAPI.Vocabulary.Subject;
let i;
for (i = 0; i < full_Concepts.length; i++) {
global.ID = full_Concepts[i].Subject_ID;
searchTermDetails(global.ID);
} return nestedFunction(full_Concepts);
});
}
app.get('/subjectID', function(req, res) {
let selectedID = req.query.subjectID;
searchTermDetails(selectedID, function (termDetails) {
res.json(termDetails);
});
});
2nd API : http://vocabsservices.getty.edu/AATService.asmx/AATGetSubject?subjectID=300004838
function searchTermDetails(selectedID, nestedFunction) {
selectedID = global.ID;
let URL_Details = baseURL + "AATGetSubject?" + "subjectID=" + selectedID;
fetch(URL_Details)
.then(function (response) {
return response.text();
}).then(function (response) {
APICallResults_new = response;
parseJsonAgain();
let detailAPI = JSON.parse(APICallResults_new);
let new_details = detailAPI.Vocabulary.Subject;
let Subject_ID = new_details[0].Subject_ID;
let descriptive_Notes_English = new_details[0].Descriptive_Notes[0].Descriptive_Note[0].Note_Text;
} **return nestedFunction(new_details);**
}).catch(function (error) {
console.log("error");
});
}
function parseJson() {
xml2js.parseString(APICallResults, {object: true, trim:true, sanitize: true, arrayNotation: true, mergeAttrs: true}, (err, result) => {
if (err) {
throw err;
}
const resultJson = JSON.stringify(result, null, 4);
//JSON.parse(resultJson);
APICallResults = resultJson;
});
}
function parseJsonAgain() {
xml2js.parseString(APICallResults_new, {object: true, trim:true, sanitize: true, arrayNotation: true, mergeAttrs: true}, (err, result) => {
if (err) {
throw err;
}
const resultJsonAgain = JSON.stringify(result, null, 4);
APICallResults_new = resultJsonAgain;
//console.log(APICallResults_new);
});
}
I've read many threads about this error but the proposed solutions don't seem to work.
In here:
for (i = 0; i < full_Concepts.length; i++) {
global.ID = full_Concepts[i].Subject_ID;
searchTermDetails(global.ID);
}
where you call searchTermDetails(), you are not passing the nestedFunction second argument. Thus, when searchTermDetails() tries to use that argument, it causes the error you get.
You can either add the callback to this call or, if the callback could be optional, then you can modify searchTermDetails to check to see if the second argument is a function and, if not, then don't try to call it.
I am running into a sync/async issue with my program and am having a hard time implementing async into my middleware. I have tried a few times and broken it a bunch, what would be a fairly simple way to implement async await?
Here is the route
router.post("/testmedia", uploadFiles, testMedia);
Here is the middleware
const uploadFiles = (req,res,next)=> {
let b2fileIDs = [];
let savedPhotoLinks = {};
upload(req,res,function(err){
req.files.forEach(function(image){
var b2accountId;
var b2authToken;
var b2uploadURL;
//console.log(image);
b2.authorize()
.then(function(auth){
b2accountId = auth.accountId;
b2authToken = auth.authorizationToken;
}).then(function(ret){
b2.getUploadUrl(process.env.B2BUCKET_ID)
}).then(function(cb){
b2uploadURL = cb.uploadUrl;
b2authToken = cb.authorizationToken;
}).then(function(cb){
b2.uploadFile({
uploadUrl: b2uploadURL,
uploadAuthToken: b2authToken,
filename: 'test',
data: image.buffer
}).then(function(cb){
var uploadLink = {link: cb.fileId};
b2fileIDs.push(uploadLink);
}).then(function(){
successFn(response){
console.log(b2fileIDs);
//Save ID's to DB here
}
}).catch((error) => {
console.log("This is an ERROR " + error );
});
})
console.log("1");
console.log('2');
})
console.log('3');
console.log(b2fileIDs);
})
console.log('4');
next();
};
The problem is that when I upload multiple files the async nature moves on without capturing the b2fileIDs line.
How do I get the data from a https request outside of its scope?
Update
I've seen Where is body in a nodejs http.get response?, but it doesn't answer this question. In fact, that question isn't answered accurately, either. In the accepted answer (posted by the asker), a third party library is used. Since the library returns an object different from that returned by http.get() it doesn't answer the question.
I tried to set a variable to the return value of http.get() using await, but that returns a http.clientRequest and doesn't give me access to the response data that I need.
I'm using Node v8.9.4 with Express and the https module to request data from Google's Custom Search.
I have two routes. One for a GET request and one for a POST request used when submitting a form on the front page. They both basically serve the same purpose... request the data from CSE and present the data as a simple JSON string. Rather than repeat myself, I want to put my code for the CSE request into a function and just call the function within the callback for either route.
I thought about returning all the way up from the innermost callback, but that won't work because it wouldn't get to the request's error event handler or the necessary .end() call.
Here's a subset of the actual code:
app.get('/api/imagesearch/:query', newQuery)
app.post('/', newQuery)
function newQuery (req, res) {
let query = req.body.query || req.params.query
console.log(`Search Query: ${query}`)
res.status(200)
res.set('Content-Type', 'application/json')
// This doesn't work
let searchResults = JSON.stringify(cseSearch(req))
res.end(searchResults)
}
function cseSearch (request) {
let cseParams = '' +
`?q=${request.params.query}` +
`&cx=${process.env.CSE_ID}` +
`&key=${process.env.API_KEY}` +
'&num=10' +
'&safe=high' +
'&searchType=image' +
`&start=${request.query.offset || 1}`
let options = {
hostname: 'www.googleapis.com',
path: '/customsearch/v1' + encodeURI(cseParams)
}
let cseRequest = https.request(options, cseResponse => {
let jsonString = ''
let searchResults = []
cseResponse.on('data', data => {
jsonString += data
})
cseResponse.on('end', () => {
let cseResult = JSON.parse(jsonString)
let items = cseResult.items
items.map(item => {
let resultItem = {
url: item.link,
snippet: item.title,
thumbnail: item.image.thumbnailLink,
context: item.image.contextLink
}
searchResults.push(resultItem)
})
// This doesn't work... wrong scope, two callbacks deep
return searchResults
})
})
cseRequest.on('error', e => {
console.log(e)
})
cseRequest.end()
}
If you're curious, it's for a freeCodeCamp project: Image Search Abstraction Layer
using promise method solve this issue.
cseSearch(req).then(searchResults=>{
res.end(searchResults)
}).catch(err=>{
res.status(500).end(searchResults)
})
function cseSearch (request) {
return new Promise((resolve, reject)=>{
...your http request code
cseResponse.on('end', () => {
let cseResult = JSON.parse(jsonString)
let items = cseResult.items
items.map(item => {
let resultItem = {
url: item.link,
snippet: item.title,
thumbnail: item.image.thumbnailLink,
context: item.image.contextLink
}
searchResults.push(resultItem)
})
resolve(searchResults);
})
})
}
Based on what I explained in the comments, to give you an idea how compact your code could be using the request-promise library, here's what you could use:
const rp = require('request-promise-native');
app.get('/api/imagesearch/:query', newQuery)
app.post('/', newQuery)
function newQuery (req, res) {
let query = req.body.query || req.params.query
console.log(`Search Query: ${query}`)
cseSearch(req).then(results => {
res.json(results);
}).catch(err => {
console.log("newQueryError ", err);
res.sendStatus(500);
});
}
function cseSearch (request) {
let cseParams = '' +
`?q=${request.params.query}` +
`&cx=${process.env.CSE_ID}` +
`&key=${process.env.API_KEY}` +
'&num=10' +
'&safe=high' +
'&searchType=image' +
`&start=${request.query.offset || 1}`
let options = {
hostname: 'www.googleapis.com',
path: '/customsearch/v1' + encodeURI(cseParams),
json: true
};
return rp(options).then(data => {
return data.items.map(item => {
return {
url: item.link,
snippet: item.title,
thumbnail: item.image.thumbnailLink,
context: item.image.contextLink
};
});
});
Ok, so I searched for a while, but I couldn't find any information on how to delete all messages in a discord channel. And by all messages I mean every single message ever written in that channel. Any clues?
Try this
async () => {
let fetched;
do {
fetched = await channel.fetchMessages({limit: 100});
message.channel.bulkDelete(fetched);
}
while(fetched.size >= 2);
}
Discord does not allow bots to delete more than 100 messages, so you can't delete every message in a channel. You can delete less then 100 messages, using BulkDelete.
Example:
const Discord = require("discord.js");
const client = new Discord.Client();
const prefix = "!";
client.on("ready" () => {
console.log("Successfully logged into client.");
});
client.on("message", msg => {
if (msg.content.toLowerCase().startsWith(prefix + "clearchat")) {
async function clear() {
msg.delete();
const fetched = await msg.channel.fetchMessages({limit: 99});
msg.channel.bulkDelete(fetched);
}
clear();
}
});
client.login("BOT_TOKEN");
Note, it has to be in a async function for the await to work.
Here's my improved version that is quicker and lets you know when its done in the console but you'll have to run it for each username that you used in a channel (if you changed your username at some point):
// Turn on Developer Mode under User Settings > Appearance > Developer Mode (at the bottom)
// Then open the channel you wish to delete all of the messages (could be a DM) and click the three dots on the far right.
// Click "Copy ID" and paste that instead of LAST_MESSAGE_ID.
// Copy / paste the below script into the JavaScript console.
var before = 'LAST_MESSAGE_ID';
var your_username = ''; //your username
var your_discriminator = ''; //that 4 digit code e.g. username#1234
var foundMessages = false;
clearMessages = function(){
const authToken = document.body.appendChild(document.createElement`iframe`).contentWindow.localStorage.token.replace(/"/g, "");
const channel = window.location.href.split('/').pop();
const baseURL = `https://discordapp.com/api/channels/${channel}/messages`;
const headers = {"Authorization": authToken };
let clock = 0;
let interval = 500;
function delay(duration) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), duration);
});
}
fetch(baseURL + '?before=' + before + '&limit=100', {headers})
.then(resp => resp.json())
.then(messages => {
return Promise.all(messages.map((message) => {
before = message.id;
foundMessages = true;
if (
message.author.username == your_username
&& message.author.discriminator == your_discriminator
) {
return delay(clock += interval).then(() => fetch(`${baseURL}/${message.id}`, {headers, method: 'DELETE'}));
}
}));
}).then(() => {
if (foundMessages) {
foundMessages = false;
clearMessages();
} else {
console.log('DONE CHECKING CHANNEL!!!')
}
});
}
clearMessages();
The previous script I found for deleting your own messages without a bot...
// Turn on Developer Mode under User Settings > Appearance > Developer Mode (at the bottom)
// Then open the channel you wish to delete all of the messages (could be a DM) and click the three dots on the far right.
// Click "Copy ID" and paste that instead of LAST_MESSAGE_ID.
// Copy / paste the below script into the JavaScript console.
// If you're in a DM you will receive a 403 error for every message the other user sent (you don't have permission to delete their messages).
var before = 'LAST_MESSAGE_ID';
clearMessages = function(){
const authToken = document.body.appendChild(document.createElement`iframe`).contentWindow.localStorage.token.replace(/"/g, "");
const channel = window.location.href.split('/').pop();
const baseURL = `https://discordapp.com/api/channels/${channel}/messages`;
const headers = {"Authorization": authToken };
let clock = 0;
let interval = 500;
function delay(duration) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), duration);
});
}
fetch(baseURL + '?before=' + before + '&limit=100', {headers})
.then(resp => resp.json())
.then(messages => {
return Promise.all(messages.map((message) => {
before = message.id;
return delay(clock += interval).then(() => fetch(`${baseURL}/${message.id}`, {headers, method: 'DELETE'}));
}));
}).then(() => clearMessages());
}
clearMessages();
Reference: https://gist.github.com/IMcPwn/0c838a6248772c6fea1339ddad503cce
This will work on discord.js version 12.2.0
Just put this inside your client on message event
and type the command: !nuke-this-channel
Every message on channel will get wiped
then, a kim jong un meme will be posted.
if (msg.content.toLowerCase() == '!nuke-this-channel') {
async function wipe() {
var msg_size = 100;
while (msg_size == 100) {
await msg.channel.bulkDelete(100)
.then(messages => msg_size = messages.size)
.catch(console.error);
}
msg.channel.send(`<#${msg.author.id}>\n> ${msg.content}`, { files: ['http://www.quickmeme.com/img/cf/cfe8938e72eb94d41bbbe99acad77a50cb08a95e164c2b7163d50877e0f86441.jpg'] })
}
wipe()
}
This will work so long your bot has appropriate permissions.
module.exports = {
name: "clear",
description: "Clear messages from the channel.",
args: true,
usage: "<number greater than 0, less than 100>",
execute(message, args) {
const amount = parseInt(args[0]) + 1;
if (isNaN(amount)) {
return message.reply("that doesn't seem to be a valid number.");
} else if (amount <= 1 || amount > 100) {
return message.reply("you need to input a number between 1 and 99.");
}
message.channel.bulkDelete(amount, true).catch((err) => {
console.error(err);
message.channel.send(
"there was an error trying to prune messages in this channel!"
);
});
},
};
In case you didn't read the DiscordJS docs, you should have an index.js file that looks a little something like this:
const Discord = require("discord.js");
const { prefix, token } = require("./config.json");
const client = new Discord.Client();
client.commands = new Discord.Collection();
const commandFiles = fs
.readdirSync("./commands")
.filter((file) => file.endsWith(".js"));
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
client.commands.set(command.name, command);
}
//client portion:
client.once("ready", () => {
console.log("Ready!");
});
client.on("message", (message) => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const commandName = args.shift().toLowerCase();
if (!client.commands.has(commandName)) return;
const command = client.commands.get(commandName);
if (command.args && !args.length) {
let reply = `You didn't provide any arguments, ${message.author}!`;
if (command.usage) {
reply += `\nThe proper usage would be: \`${prefix}${command.name} ${command.usage}\``;
}
return message.channel.send(reply);
}
try {
command.execute(message, args);
} catch (error) {
console.error(error);
message.reply("there was an error trying to execute that command!");
}
});
client.login(token);
Another approach could be cloning the channel and deleting the one with the messages you want deleted:
// Clears all messages from a channel by cloning channel and deleting old channel
async function clearAllMessagesByCloning(channel) {
// Clone channel
const newChannel = await channel.clone()
console.log(newChannel.id) // Do with this new channel ID what you want
// Delete old channel
channel.delete()
}
I prefer this method rather than the ones listed on this thread because it most likely takes less time to process and (I'm guessing) puts the Discord API under less stress. Also, channel.bulkDelete() is only able to delete messages that are newer than two weeks, which means you won't be able to delete every channel message in case your channel has messages that are older than two weeks.
The possible downside is the channel changing id. In case you rely on storing ids in a database or such, don't forget to update those documents with the id of the newly cloned channel!
Here's #Kiyokodyele answer but with some changes from #user8690818 answer.
(async () => {
let deleted;
do {
deleted = await channel.bulkDelete(100);
} while (deleted.size != 0);
})();
For clarity I have other cloud functions that all run intermittently (i.e from 'cold' in around 2-6 seconds, and all use the same boilerplate set up of importing an admin instance and exporting the function as a module)
I've seen other similar posts but this is really bugging me. I have a cloud function like so:
const admin = require('../AdminConfig');
const { reportError } = require('../ReportError');
module.exports = (event) => {
const uid = event.params.uid;
const snapshot = event.data;
if (snapshot._newData === null ) {
return null;
}
console.log('Create org begin running: ', Date.now());
const organisation = event.data.val();
const rootRef = admin.database().ref();
const ref = rootRef.child('/organisations').push();
const oid = ref.key;
const userData = {
level: 'owner',
name: organisation.name,
};
const orgShiftInfo = {
name: organisation.name,
startDay: organisation.startDay || 'Monday',
};
const updatedData = {};
updatedData[`/users/${uid}/currentOrg`] = oid;
updatedData[`/users/${uid}/organisations/${oid}`] = userData;
updatedData[`/organisations/${oid}`] = organisation;
updatedData[`/org_shift_info/${oid}`] = orgShiftInfo;
rootRef.update(updatedData, (err) => {
if (err) {
return rootRef.child(`/users/${uid}/addOrgStatus`).set({ error: true })
.then(() => {
console.log(`error adding organisation for ${uid}: `, err);
return reportError(err, { uid });
});
}
console.log('Create org wrote succesfully: ', Date.now());
return rootRef.child(`/users/${uid}/addOrgStatus`).set({ success: true });
});
}
I understand the 'cold start' thing but I think something is seriously wrong that it's taking 25 seconds. The logs don't return any error and are as so:
Is there some deeper way I can debug this to try and figure out why it's taking so long? It's unusable at the moment. Thanks a lot.
Solved:
Sorry,
I misunderstood the API a bit. I should have watched the promise video first!
I needed to put
return rootRef.update...
instead of
rootRef.update...