Puppeteer + Discord.js issues with an auto invite command - discord

I'm working on a small project to learn puppeteer and discord bots, thought it would be interesting to combine the two and make a weird cyborg bot thing that is essentially a bot but a normal user. I've set up the command handlers, etc, and I've created an 'invite' command that takes one of the arguments that the person provides (the code of the server invite) and uses it to auto-invite itself.
for example typing in discord: <prefix>invite 78uHjkaf
This semi-works. Not really. It'll work the first time, but never again and I'll have to restart the thing. I keep trying to add a page.close(); hoping that it will solve the problem but then it won't work at all. How can I make this work consistently and reusably? - Note that my command handler works fine. The entire issue is located within the following code. (I hope)
Semi reliable.
if (command === "invite") {
const invite = async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://discordapp.com/');
let [link] = args;
await page.evaluate(async ({link}) => {
const x = new XMLHttpRequest();
x.open(`POST`, `https://discordapp.com/api/v6/invites/` + `${link}`);
x.setRequestHeader('Authorization', 'my token');
x.send();
},{link});
};
invite();
}
Doesn't work at all.
if (command === "invite") {
const invite = async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://discordapp.com/');
let [link] = args;
await page.evaluate(async ({link}) => {
const x = new XMLHttpRequest();
x.open(`POST`, `https://discordapp.com/api/v6/invites/` + `${link}`);
x.setRequestHeader('Authorization', 'my token');
x.send();
},{link});
await page.close();
};
invite();
}
I am REALLY stumped.
Update.
For some reason shifting
x.open(`POST`, `https://discordapp.com/api/v6/invites/` + `${link}`);
to
x.open(`POST`, `https://discordapp.com/api/v6/invites/${link}`);
fixed the problem of unreliability. Adding a close page still kills it though. I feel like that should be added however?

Why are you using Puppeteer? It's easier to use the Discord private API instead.
const fetch = require('node-fetch'); // npm install node-fetch
if (command === "invite") {
const [code] = args; // should only be the code, not full URL (8KqbkST, not https://discord.gg/8KqbkST)
fetch(`https://discord.com/api/v8/invites/${code}`, {
method: 'POST',
headers: { 'authorization': 'YOUR USER TOKEN' },
body: JSON.stringify({
code,
new_member: true
})
});
}
By the way this is totally forbidden by the Discord T.O.S and you should not use it for massive use such as joining a lot of servers at once (because it is forbidden, and because this type of behavior is very painful on Discord).

Use this 100%
const fetch = require('node-fetch'); // npm install node-fetch
if (command === "invite") {
const [code] = args; // should only be the code, not full URL (8KqbkST, not https://discord.gg/8KqbkST)
fetch(https://discord.com/api/v8/invites/${code}, {
method: 'POST',
headers: { 'authorization': 'YOUR USER TOKEN' },
body: JSON.stringify({
code,
new_member: true
})
});
}

Related

Discord.js Ping Latency Inside Embed from Slash Command

Newbie here. I am trying to get a slash command to send an embed with the amount of time it took between the initial message and the message response time. I am getting TypeError: Cannot read properties of undefined (reading 'createdTimestamp') and Error [INTERACTION_ALREADY_REPLIED]: The reply to this interaction has already been sent or deferred. I've jumped around looking at others code trying to find a way to make this work but slash command handling still doesn't make a lot of sense to me. My code block is below. I followed along with https://discordjs.guide so if you have any other suggestions with structure feel free to comment them below. Thank you!
const { SlashCommandBuilder } = require('#discordjs/builders');
const { MessageEmbed } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with Pong!'),
async execute(interaction) {
await interaction.reply("Pinging bot...").then (async (msg) =>{
const exampleEmbed = new MessageEmbed()
.setColor('0x0000ff')
.setTitle('Pong! :ping_pong:')
.addField("Time taken: ", `${msg.createdTimestamp - message.createdTimestamp}`)
.setThumbnail("https://78.media.tumblr.com/be43242341a7be9d50bb2ff8965abf61/tumblr_o1ximcnp1I1qf84u9o1_500.gif")
interaction.editReply({ embeds: [exampleEmbed] });
})
},
};
first you need to fetch the reply you send, u can use fetchReply to get the interaction reply. instead of replying with "Pinging bot..." you can defer the reply and then use the createdTimestamp. A basic example of a ping command would be
const { SlashCommandBuilder } = require('#discordjs/builders');
module.exports = {
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with Pong!'),
async execute(interaction) {
const sent = await interaction.reply({ content: 'Pinging...', fetchReply: true });
await interaction.editReply(`:ping_pong: Pong!\n:stopwatch: Uptime: ${Math.round(interaction.client.uptime / 60000)} minutes\n:sparkling_heart: Websocket heartbeat: ${interaction.client.ws.ping}ms.\n:round_pushpin: Rountrip Latency: ${sent.createdTimestamp - interaction.createdTimestamp}ms`);
},
};
You can customize the response into an embed or however you like. The djs guide has a section on ping command here

Fetching an array of ID's with promise.all() not working

I am making a series of api requests to return objects based on their id, using promise.all().
However, i'm getting 404's, which I think is due to the way i've added my headers (as i tried with another simpler API that doesn't need authentication and it works fine).
The API i am accessing is the UDEMY api. Documentation here: https://www.udemy.com/developers/affiliate/
Can anyone help me make this work with the headers?
Thanks.
getCourseData = () => {
let courseIDs = this.state.beginnerCourseIDs;
let headers = new Headers();
headers.append('Authorization', 'Basic ' + base64.encode("XXUSERKEY:XXSECRETKEY"))
let requests = courseIDs.map(course => {
return fetch(`https://cors-anywhere.herokuapp.com/https://www.udemy.com/api-2.0/courses/${course}/, { headers: ${headers} }` )
});
Promise.all(requests)
.then(responses => responses.forEach(
response => console.log(response)
));
}
Currently your headers object is forming part of your url string.
You will need to add your headers to the options object, the second argument to fetch:
let requests = courseIDs.map(course => {
return fetch(`https://cors-anywhere.herokuapp.com/https://www.udemy.com/api-2.0/courses/${course}/`, { headers })
});
Also, if the API returns JSON then you will need to parse the response as a JSON object:
let requests = courseIDs.map(course => {
return fetch(`https://cors-anywhere.herokuapp.com/https://www.udemy.com/api-2.0/courses/${course}/`, { headers })
.then(res => res.json())
});
Hopefully this solves your problem.

How to send image to server with Blob type using Axios in React Native?

I try to select a picture from the gallery, and I got data like below:
{
"exif":null,
"localIdentifier":"9F983DBA-EC35-42B8-8773-B597CF782EDD/L0/001",
"filename":"IMG_0003.JPG",
"width":500,
"modificationDate":null,
"mime":"image/jpeg",
"sourceURL":"file:///Users/vichit/Library/Developer/CoreSimulator/Devices/3BBFABAC-2171-49AA-8B2B-8C2764949258/data/Media/DCIM/100APPLE/IMG_0003.JPG",
"height":500,
"creationDate":"1344451932"
}
For this time, I want to send this picture to the server with Blob type using Axios.
I don't know how to convert this picture to a Blob type.
It's simple:
async function uploadToServer(sourceUrl) {
// first get our hands on the local file
const localFile = await fetch(sourceUrl);
// then create a blob out of it (only works with RN 0.54 and above)
const fileBlob = await localFile.blob();
// then send this blob to filestack
const serverRes = await fetch('https://www.yourAwesomeServer.com/api/send/file', { // Your POST endpoint
method: 'POST',
headers: {
'Content-Type': fileBlob && fileBlob.type,
},
body: fileBlob, // This is your file object
});
const serverJsonResponse = await serverRes.json();
// yay, let's print the result
console.log(`Server said: ${JSON.stringify(serverJsonResponse)}`);
}
And run like uploadToServer("file:///Users/vichit/Library/Developer/CoreSimulator/Devices/3BBFABAC-2171-49AA-8B2B-8C2764949258/data/Media/DCIM/100APPLE/IMG_0003.JPG")
It took me forever to figure it out, but it makes sense now.
I hope that helps someone!

Passing an object from React to Express and creating a new Airtable record

I’m having trouble passing an object from React to Express, and then creating an airtable record in Express.
In react, i am sending an http request to Express via:
finalSubmit() {
const airtableObj = {
title: 'hi',
}
fetch('api/submit',{
method: 'POST',
body: JSON.stringify(airtableObj),
headers: {"Content-Type": "application/json"}
})
}
My Express code is:
app.post('/api/submit', jsonParser, async (req, res) => {
const newStudy = JSON.stringify(req.body);
await console.log(newStudy);
table.create(newStudy, function(err, record) {
if (err) {console.log(err); res.json(err)} else {console.log(record), res.json('Success!')}
});
})
However, I keep getting errors back from the airtable api. IF I replace the 4th line of my express code with:
table.create({“title”:“hi”}
instead of
table.create(newStudy)
, everything works fine. It seems like this should work according to the airtable documentationt... (https://airtable.com/api). Is there something I am doing wrong with how I am manipulating my data in and out of JSON?
Thanks
This appears to be happening because you're calling JSON.stringify(req.body), which you don't need to do.
table.create takes an object, not a string, so you'll want to do something like this:
const newStudy = req.body;
table.create(newStudy, function(err, record) {
// ...
});
I found a solution, not sure if it's a very good one tho...
app.post('/api/submit', jsonParser, async (req, res) => {
table.create({
"title": `${req.body.post0.title}`} ...

Error: User credentials required in Google Cloud Print API

I'm trying to use Google Cloud Print(GCP) API, but I can't make it works.
Maybe I've understood bad the workflow because is the first time I'm using the google api, please help me to understand how to make it works.
Initial considerations:
I'm trying to implement it in reactJS, but It is indifferent because the logic to make GCP works is independent of the technology. Then you also can help me understand the workflow.
What exactly I want:
To make my first test, I am looking to get all information about my printer.
What I did:
I created a project in: https://console.developers.google.com
Inside the project created, I created a credential:
create credentials -> OAuth client ID
And I chose Application type: Web, and also configure the restrictions to source and redirection to my localhost.
Manually in https://www.google.com/cloudprint, I added my printer, I made a test printing a PDF and was OK.
I created a project in reactJS to get the information of my printer I've added.
Component:
Explanation:
I'm using a component react-google-login to obtain easily the user accessToken: https://github.com/anthonyjgrove/react-google-login
This component only obtains the access token and save it in localStorage, in a variable called googleToken and it draws a button to call a function to obtain the information about the printer.
code:
import React, { Component } from 'react'
import GoogleLogin from 'react-google-login';
import { connect } from 'react-redux'
import { getPrinters } from '../actions/settings'
class Setting extends Component {
responseGoogle(response) {
const accessToken = response.accessToken
localStorage.setItem('googleToken', accessToken)
}
render() {
return (
<div>
<GoogleLogin
clientId="CLIENT_ID_REMOVED_INTENTIONALLY.apps.googleusercontent.com"
buttonText="Login"
onSuccess={this.responseGoogle}
onFailure={this.responseGoogle}
/>
<button
onClick = {() => {
this.props.getPrinters()
}}
>test printer</button>
</div>
)
}
}
const mapStateToProps = state => {
return {
state: state
}
}
const mapDispatchToProps = dispatch => {
return {
getPrinters() {
dispatch(getPrinters())
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Setting)
Action or Function to get information printer:
Explanation:
I'm passing the parameter printerid to get information about that printer.
In authorization, I'm using OAuth ... because in the documentation says that(second paragraph).: https://developers.google.com/cloud-print/docs/appInterfaces
The next two headers I wrote it because I tried solutions as:
Google Cloud Print API: User credentials required
Google Cloud Print User credentials required
code:
import axios from 'axios'
axios.defaults.headers.common['Authorization'] = 'OAuth ' + localStorage.getItem('googleToken')
axios.defaults.headers.common['scope'] = 'https://www.googleapis.com/auth/cloudprint'
axios.defaults.headers.common['X-CloudPrint-Proxy'] = 'printingTest'
const getPrinters = () => {
return () => {
return axios.get('https://www.google.com/cloudprint/printer'
, {
params: {
printeid: 'PRINTER_ID_REMOVED_INTENTIONALLY'
}
}
)
.then(response => {
console.log('response of google cloud print')
console.log(response)
})
}
}
export { getPrinters }
Error:
After all explained before, I got the next error:
User credentials required
Error 403
Note:
I'm using CORS plugin by recommendation of:
Chrome extensions for silent print?
because initially, I had cors error.
Any suggestion or recommendation would be very useful, thanks.
I've resolved my problem, my main problem about User Credential required were because I was using the incorrect access token and It was because I was getting the access token incorrectly.
I'm going to explain my whole solution because there are few examples of codes with this API.
Solutions:
The steps described were Ok until the fourth step where I used the external component react-google-login to trying to get the access token, instead I used googleapis module: Link Github googleapis
Also to avoid CORS problem(and not use CORS chrome plugin) I wrote the requests to Google API in server side.(NODEJS)
I had also a problem in the frontend when I tried to generate a popup to give permission for printer(problems about CORS), my solution was to use this very simple module for authentication: Link Github oauth-open
General scheme:
Explanation:
Knowing I have all data described in my question post(until the third step).
Authentication:
The next step in getting a URL and use it to the user can authenticate.
As I said before I used the module oauth-open in the frontend to generate the popup and only this module need the URL. To get the URL in the backend I used the endpoint /googleurl, where here I used the method generateAuthUrl of the module googleapis to generate the URL.
After that In the frontend, I got the authentication_code(that returned the module oauth-open), I send It to my endpoint /googletoken and here I process the authentication_code to generate access token, refresh token and expiration date with the method getToken of the module googleapis. Finally, these data are stored in the database.
Print:
For print, since the frontend, I send what data I need send to the printer. I used my endpoint /print
In the backend endpoint, my logic was the next:
Recover tokens and expiration date from database, with the expiration date check if the token has expired, and if It has already expired then gets another token and replace the old access token with the new one, replacing also with the new expiration date, to obtain this new data only is necessary call to method refreshAccessToken of module googleapis.Note: the refresh token never expires.
After having the access token updated, use it to send data to the printer with Google route(.../submit)
Code:
All the next codes are in only 1 file
Some data as validation, static variables, error handler, etc, has been removed to better understanding.
Route get URL authentication.
const express = require('express');
const google = require('googleapis');
const router = express.Router();
var OAuth2 = google.auth.OAuth2;
const redirect_url = 'http://localhost:3001/setting'; //Your redirect URL
var oauth2Client = new OAuth2(
'CLIENT ID', //Replace it with your client id
'CLIEND SECRET', //Replace it with your client secret
redirect_url
);
var url = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: 'https://www.googleapis.com/auth/cloudprint'
});
router.get('/googleurl', (req, res) => {
return res.status(200).send({
result: { googleURLToken: url }
});
});
To get tokens using the authentication code and save these in the database.
const Setting = require('../models/setting'); // My model(Mongoose)
router.post('/googletoken', (req, res) => {
oauth2Client.getToken(req.body.code, function (err, tokens) {
oauth2Client.credentials = tokens;
// If refresh token exits save it
// because the refresh token it returned only 1 time! IMPORTANT
if (tokens.hasOwnProperty('refresh_token')) {
let setting = new Setting();
setting.refreshTokenGoogle = tokens.refresh_token;
setting.expirationTokenGoogle = tokens.expiry_date;
setting.tokenGoogle = tokens.access_token;
setting.save()
.then((settingCreated) => {
return res.status(200).send({
message: 'OK'
});
})
}
});
});
To print
const axios = require('axios');
const moment = require('moment');
router.post('/print',async (req, res) => {
const tickeProperties = {
'version': '1.0',
'print': {
'vendor_ticket_item': [],
'color': { 'type': 'STANDARD_MONOCHROME' },
'copies': { 'copies': 1 }
}
};
const accessToken = await getTokenGoogleUpdated();
axios.get(
'https://www.google.com/cloudprint/submit',
{
params: {
printerid : printerID, // Replace by your printer ID
title: 'title printer',
ticket: tickeProperties,
content : 'print this text of example!!!',
contentType: 'text/plain'
},
headers: {
'Authorization': 'Bearer ' + accessToken
}
}
)
.then(response => {
return res.status(200).send({
result: response.data
});
})
}
);
async function getTokenGoogleUpdated() {
return await Setting.find({})
.then(async setting => {
const refreshTokenGoogle = setting[0].refreshTokenGoogle;
const expirationTokenGoogle = setting[0].expirationTokenGoogle;
const tokenGoogle = setting[0].tokenGoogle;
const dateToday = new Date();
// 1 minute forward to avoid exact time
const dateTodayPlus1Minute = moment(dateToday).add(1, 'm').toDate();
const dateExpiration = new Date(expirationTokenGoogle);
// Case date expiration, get new token
if (dateExpiration < dateTodayPlus1Minute) {
console.log('Updating access token');
oauth2Client.credentials['refresh_token'] = refreshTokenGoogle;
return await oauth2Client.refreshAccessToken( async function(err, tokens) {
// Save new token and new expiration
setting[0].expirationTokenGoogle = tokens.expiry_date;
setting[0].tokenGoogle = tokens.access_token;
await setting[0].save();
return tokens.access_token;
});
} else {
console.log('Using old access token');
return tokenGoogle;
}
})
.catch(err => {
console.log(err);
});
}
I hope It helps you if you want to use Google Cloud Print to not waste a lot of time as I did.
The important part there is a scope https://www.googleapis.com/auth/cloudprint which is not obvious and took one day for me to figure out.

Resources