Using SmtpJS in a userscript: Doesn't work, no error messages? - greasemonkey

I'm trying to send an email through a userscript using smtpjs because it seems the easiest approach. However it seems more difficult than just sending it by javascript that is embedded in a HTML page. Using this userscript (based on the smtpjs website) I get no error in the console and no email is sent, is this a framework issue or am I missing something here? (if you suggest an easier way to send emails within a userscript don't hesitate to share)
// ==UserScript==
// #name New Userscript
// #namespace http://tampermonkey.net/
// #version 0.1
// #description try to take over the world!
// #author You
// #match *
// #grant none
// #require http://smtpjs.com/smtp.js
// ==/UserScript==
if (confirm("send mail?")) {
Email.send("FROM#gmail.com",
"TO#gmail.com",
"This is a subject",
"this is the body",
"smtp.gmail.com",
"USER",
"PW");
}
(I tried gmailAPI (pure JS version doesn't support sending emails?) and emailjs framework without success in userscripts)

If you look at the smtpjs.com source, it creates a post request url, then appends it to the document inside of a <link>. This won't work on secure pages.
/* SmtpJS.com */
Email = {
send: function (t, e, o, n, d, r, c) {
var a = Math.floor(1e6 * Math.random() + 1),
m = "http://smtpjs.com/smtp.aspx?";
m += "From=" + t,
m += "&to=" + e,
m += "&Subject=" + encodeURIComponent(o),
m += "&Body=" + encodeURIComponent(n),
void 0 == d.token ?
(m += "&Host=" + d, m += "&Username=" + r, m += "&Password=" + c, m += "&Action=Send") :
(m += "&SecureToken=" + d.token, m += "&Action=SendFromStored"),
m += "&cachebuster=" + a,
Email.addScript(m)
},
addScript: function (t) {
var e = document.createElement("link");
e.setAttribute("rel", "stylesheet"),
e.setAttribute("type", "text/xml"),
e.setAttribute("href", t),
document.body.appendChild(e)
}
};
You could use most of the above code... keep the send function, but replace the addScript function with a GM_xmlhttpRequest to post the data to their server.

Related

Node fluent-ffmpeg screenshots on Windows

The fluent-ffmpeg package has a function that will create screenshots at even intervals in a video for a given count. The problem with this is that it often spawns a ENAMETOOLONG error on Windows.
To get around this, I copied the relevant parts of the source code that I needed and instead of running it all as one long command, split it up into n many commands (where n is the amount of screenshots).
This seems to work at first. It (sometimes) gets the first screenshot, but then it throws the same ENAMETOOLONG error. How can I get around this?
Here is what I have attmpted:
const count = 50 // just an example
const dest = path.join(os.tmpdir(), `screenshots_${Date.now()}`)
const interval = 100 / (1 + count)
let timemarks = Array(count)
.fill(0).map((_, i) => (interval * (i + 1)) + '%')
.map(mark => {
if (('' + mark).match(/^([\d.]+)%$/)) {
// #ts-ignore
return videoDuration * parseFloat(mark) / 100
} else {
return mark
}
})
.map(mark => {
if (typeof mark === 'number') {
return mark
}
if (mark.indexOf(':') === -1 && mark.indexOf('.') >= 0) {
return Number(mark)
}
const parts = mark.split(':')
let secs = Number(parts.pop())
if (parts.length) {
secs += Number(parts.pop()) * 60
}
if (parts.length) {
secs += Number(parts.pop()) * 3600
}
return secs
})
.sort((a, b) => a - b)
// my first attempt:
for (const mark of timemarks) {
this.video
.screenshot({
count: 1,
folder: dest,
timemarks: [mark],
})
}
// second attempt:
let i = 0
for (const mark of timemarks) {
const filename = `frame_${i.toString().padStart(count.toString().length, '0')}.jpg`
this.video
.setStartTime(mark)
.output(path.join(dest, filename))
.frames(1)
.run()
i++
}
For context, this.video is just ffmpeg(videoFilePath).
My first attempt, as I previously stated, just throws the same error and doesn't really get anywhere. Sometimes I get the first screenshot, most times I do not.
The second attempt doesn't even do anything. It seems to take it's time doing something, but I don't actually get any screenshots. If they saved anywhere, I don't know where.
Is anyone able to help me with this?

reading a 2048-bit Diffie-Hellman prime number from a buffer

I am trying to use the telegram API to support telegram calls directly from out system. In order to perform the call, the library requires e2e encryption. so I am calling a method to get a 2048-bit Diffie-Hellman prime number from the api. However, the number is coming as a buffer and I want to convert it to be a 2048bit prime number. here is the code
async requestCall(userId: string) {
try {
const dhConfig = await this.invoke(
new Api.messages.GetDhConfig({
randomLength: 32
})
);
// convert p to number
// #ts-expect-error ignore
const p = dhConfig.p as Buffer;
// convert p to integer
const pInt = p.readInt32BE(0);
// #ts-expect-error ignore
const g = dhConfig.g;
// chose random value between 1 and p-1
const a = Math.floor(Math.random() * (pInt - 1)) + 1;
// calculate g^a mod p
const ga = Math.pow(g, a) % pInt;
this.ga = ga;
// get the ga hash SHA256 of g^a mod p as 32 bytes
const gaHash = crypto.createHash('sha256').update(ga.toString()).digest();
// save in the session
this.a = a;
this.p = pInt;
// request the call
await this.invoke(
new Api.phone.RequestCall({
userId,
randomId: Math.floor(Math.random() * 100000000),
gAHash: gaHash,
protocol: new Api.PhoneCallProtocol({
udpP2p: true,
udpReflector: true,
minLayer: 65,
maxLayer: 65,
libraryVersions: ['1.0.0', '2.0.0', '3.0.0', '4.0.0']
})
})
);
} catch (err) {
console.log('[Telegram Client] [call] error: ', err);
}
}
the number that I am getting is a negative number and it is most probably wrong. I also tried to convert it to string and then number but it did not work. so, the question is, how to get the prime number from that buffer?

Limit user actions on certain functions. Uniquely identify unsigned in users

I'm working on an api that includes sending emails for password resets and email confirmations. Along with functions like "user sign up".
I'm trying to include an action limiter that allows users to perform these actions a limited amount of times within a given time frame to prevent malicious use.
At first I thought using IP addresses would be fine because even malicious users run out of ip address eventually (at least that im aware) but then I realized this might block users who are in a large building and would possibly inconvenience VPN users.
What is the best way to uniquely identify a user who is not signed in in order to limit their actions on certain functions? Is this possible? How does FAANG handle this?
Here's an example I wrote in nodejs if anybody has any feedback and/or ideas on how to make this more unique I'd be all ears.
const db = require('../../common/database');
// const ActionLimiterEnum = require('../../enums/action-limiter').actionLimiterEnum;
const NumberUtil = require('../../utils/number');
const ObjectUtil = require('../../utils/object');
// !==========================================================================================!
// This module has been put on hold until I can think of a way to uniquely identify users
// Major problem about this is that it may deny large groups of people whom use the same ip
// VPNS and/or large buildings
//
// Could cause more problems than it solves
// !==========================================================================================!
// Simple action limiter for how often a user can perform actions
// Needs to be saved to a database and not an instance because there may be multiple instances and/or they may be reset
// Object of action or "signIn"
async function actionLimiter(action,ip){
const d = {err: {code:0,message:""},res:{}}; let r,sql,vars;
r = await checkLimit(action,ip);
if(r.err.code) return r;
r = await incrementLimit(action,ip);
if(r.err.code) return r;
return d;
}
async function checkLimit(action,ip){
const d = {err: {code:0,message:""},res:{}}; let r,sql,vars;
if(action === "signIn"){
r = await checkLimit(ActionLimiterEnum.signInShortTerm,ip); if(r.err.code) return r;
r = await checkLimit(ActionLimiterEnum.signInMidTerm,ip); if(r.err.code) return r;
r = await checkLimit(ActionLimiterEnum.signInLongTerm,ip); if(r.err.code) return r;
return d;
}
const numberIp = NumberUtil.ipToNumber(ip);
var deleteDate = new Date();
deleteDate.setMilliseconds(deleteDate.getMilliseconds() - action.time);
sql = "DELETE FROM m_admin_action_limiter WHERE action_id = ? AND created_date <= ?";
vars = [action.id,deleteDate];
r = await db.query(sql,vars);
if(r.err.code) return r;
sql = "SELECT * FROM m_admin_action_limiter WHERE action_id = ? AND ip = ?";
vars = [action.id,numberIp];
r = await db.query(sql,vars);
if(r.err.code) return r;
if(r.res.length){
const results = ObjectUtil.toCamelCaseKeys(r.res[0]);
if(results.actionCount >= action.maxCount){
d.err.code = 1;
d.err.message = "Sorry this ip has performed this action too often please try again later. ";
switch(action.id){
case ActionLimiterEnum.signInShortTerm.id:
case ActionLimiterEnum.signInMidTerm.id:
case ActionLimiterEnum.signInLongTerm.id:
d.err.message += "If you're having trouble remembering your password you can reset it via email. ";
break;
default:
break;
}
d.err.actionLimited = true;
return d;
}
}
return d;
}
async function incrementLimit(action,ip){
const d = {err: {code:0,message:""},res:{}}; let r,sql,vars;
if(action === "signIn"){
r = await incrementLimit(ActionLimiterEnum.signInShortTerm,ip); if(r.err.code) return r;
r = await incrementLimit(ActionLimiterEnum.signInMidTerm,ip); if(r.err.code) return r;
r = await incrementLimit(ActionLimiterEnum.signInLongTerm,ip); if(r.err.code) return r;
return d;
}
const numberIp = NumberUtil.ipToNumber(ip);
const timenow = new Date();
sql = "SELECT admin_action_limiter_id FROM m_admin_action_limiter WHERE action_id = ? AND ip = ?";
vars = [action.id,numberIp];
r = await db.query(sql,vars);
if(r.err.code) return r;
if(r.res.length){
// update
const id = r.res[0]['admin_action_limiter_id']
sql = "UPDATE m_admin_action_limiter SET action_count = action_count + 1 WHERE admin_action_limiter_id = ?";
vars = [id];
r = await db.query(sql,vars);
if(r.err.code) return r;
}else{
// insert
sql = "INSERT INTO m_admin_action_limiter (action_id,ip,action_count,created_date) VALUES(?,?,?,?)";
vars = [action.id,numberIp,1,timenow];
r = await db.query(sql,vars);
if(r.err.code) return r;
}
return d;
}
module.exports = {
actionLimiter,
checkLimit,
incrementLimit,
Enum: ActionLimiterEnum,
};
// SQL
// -- -----------------------------------------------------
// -- Table `m_admin_action_limiter`
// -- -----------------------------------------------------
// CREATE TABLE IF NOT EXISTS `m_admin_action_limiter`(
// `admin_action_limiter_id` int(11) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
// `action_id` int(2) unsigned NOT NULL,
// `ip` int(11) unsigned NOT NULL,
// `action_count` unsigned int(11) DEFAULT 1,
// `created_date` DATETIME NOT NULL
// ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
// ALTER TABLE `m_admin_action_limiter`
// ADD CONSTRAINT m_admin_action_limiter_unique UNIQUE (`action_id`,`ip`);
// CREATE INDEX `created_date_index` ON `m_admin_action_limiter` (`created_date`);
// Enums
// time: (days * hours * minutes * seconds * milliseconds)
// time - amount of times they can try within the alotted count
// const actionLimiterEnum = {
// signInShortTerm: {
// id: 1,
// time: (1 * 1 * 60 * 60 * 1000), // 1 hour
// maxCount: 24,
// },
// signInMidTerm: {
// id: 2,
// time: (7 * 24 * 60 * 60 * 1000), // 7 days
// maxCount: 150,
// },
// signInLongTerm: {
// id: 3,
// time: (120 * 24 * 60 * 60 * 1000), // 120 days
// maxCount: 840,
// },
// authToken: {
// id: 4,
// time: (1 * 24 * 60 * 60 * 1000), // 1 day
// maxCount: 16,
// },
// createAccount: {
// id: 5,
// time: (90 * 24 * 60 * 60 * 1000), // 90 days
// maxCount: 3,
// },
// passwordCheck: {
// id: 6,
// time: (7 * 24 * 60 * 60 * 1000), // 1 week
// maxCount: 150,
// },
// }
// module.exports = {
// actionLimiterEnum,
// };
Notify users that the site will not work correctly without cookies enabled. Create the timestamp cookie when they enter the sign-up or sign-on page if it doesn't exist.. If a user requests to sign-in or sign-up and your cookie doesn't exist upon trying to read their timestamp, we know that they have cookies disabled or that it could be a malicious user.. If it exists obviously you'd compare the timestamps and update their cookie timestamp after the request logic has ran. Now if it doesn't exist tell them to enable cookies or the website won't work. This would prevent malicious use and kick IP Addresses out of the equation. If malicious users are truly a problem/concern for you the only way around the IP Address problem is with cookies, or with much more complicated logic than you currently have that attempts to identify malicious intent(could shoot yourself in the foot if it takes action against a false positive though I wouldn't recommend this route). Make sure you take steps to secure your cookie as well.
If you don't want to go the cookie route, you can run logic over client data to try and identify users with things like timezone, fonts installed, screen resolution etc.
A lot of websites require the use of cookies for full website functionality these days anyway; probably for this reason as well.
You can set up a simple key value pair database on your server. When a user requests, take things like timezone, fonts installed, screen resolution etc, and change all that data into a string without spaces, then turn it into a strong hash(a hash that would change if just one character in the string was different). The resulting hash would be the key used to identify the user. The value associated with said key would be their unique timestamp that represents the last time they accessed the server. Additionally make sure you are pulling the width and height of the actual screen, not the browser viewport.. otherwise they could resize the screen to make themself seem like a unique user if they were able to even figure out how the server identifies you. Obviously if the hash is different upon client request it's relatively safe to assume it's a new user.
With this method you wouldn't even need to use cookies. Additionally there are also loads of JS libraries that give more comprehensive client data, I suggest you check those out to build stronger unique hashes for a stronger identification of the client. To make it even better place the key value pair database on a proxy server and allow the request to the server if the conditions are met that are processed on the proxy server. Additionally you can use a service like Cloudflare to place in front of your proxy in case someone tries to DDoS the proxy server. If that happens you can get a new IP for the proxy and change it to the new IP on your DNS.

google code jam 2018 node.js boilerplate

Was anyone able to successfully submit a solution for google code jam 2018 in node.js? I'm interested in a "boilerplate" which reads and outputs the data.
The thing is whatever I tried, it says "runtime error" w/o any further details so I had to write in another language?
This solution worked for me locally.
Also I converted my solution to another language and it just worked, so I'm pretty sure the problem is in the boilerplate, not in the solution.
const fs = require('fs');
function solve(shield, program) { ... }
var content = fs.readFileSync(0, 'utf8');
var lines = content.split(/\r?\n/);
var cases = +lines[0];
var out = [];
for (var i = 1; i <= cases; i++) {
var [shield, program] = lines[i].split(' ');
var result = solve(+shield, program);
out.push(`Case #${i}: ${result === -1 ? 'IMPOSSIBLE' : result}`);
}
fs.writeFileSync(1, out.join('\n'));
The problem with your code is you are reading from file and writing to file. That's why it works for you locally.
Google Code Jam contest format has changed in 2018. You no longer read from and write to files, but instead should read from standard input and write to standard output. See the relevant FAQ section here. Google also posted a sample nodejs javascript solution for their new interactive Number Guessing problem here.
You can also refer to my solutions on GitHub. I got through the qualification round with it. I have also created tests (based on jest) for my solution. It isn't an ideal boilerplate yet, but hopefully it helps you to get started.
Reading from stdin was also a big challenge for me when I wanted to participate in the Google Code Jam 2019 using TypeScript. To make it easier in the future I developed a library with async await support. Using it you can write "blocking" code that looks like this:
// Read 2 numbers from stdin and display the sum of them.
import { StdinLineStream } from "stdin-line";
(async function() {
let inputStream = new StdinLineStream();
let [a, b] = await inputStream.getLineAsNumbers();
console.log(a + b);
inputStream.close();
})();
You will just need to use a tool like rollup to create only 1 file that you can submit to the online judge.
In order to give a comprehensive and practical answer I believe a thorough example is required:
Consider the task from Kick start 2018 round H Mural.
Down below is my solution which demonstrates how to handle input/output for the particular problem in JavaScript (NodeJS)
const fs= require('fs');
const input= fs.readFileSync(0,'utf8').trim().split('\n').slice(1)
.filter((_,i)=>i%2);
console.log(input.map((e,i)=>`Case #${i+1}: ${solve(e)}`).join('\n'));
function solve(str) {
const len= str.length,
waste= Math.floor(len/2);
let score = 0;
for (let i= waste; i<len; i++) score+= +str[i];
let maxScore= score;
for (let i= 1; i<=waste; i++) {
score+= +str[waste-i] - str[len-i];
if (score > maxScore) maxScore= score;
}
return maxScore;
}
Notes: fs.readFileSync(0,'utf8') represents an input string from stdin
and console.log() is a way to put things in stdout (we can use process.stdout.write() as an alternative instead, but I found the use of a console object is more elegant and appealing).
Nov 2020
Competition URL: https://codingcompetitions.withgoogle.com/kickstart/
Today I used this, below add 2 numbers:
process.stdin.resume();
process.stdin.setEncoding('utf-8');
var stdin_input = '';
process.stdin.on('data', function (input) {
stdin_input += input; // Reading input from STDIN
});
process.stdin.on('end', function () {
main(stdin_input);
});
function main(input) {
let inp = input.split('\n'); // NOTE: Non-Windows OS
// console.log(inp);
let T = inp[0];
let t = 0;
// console.log('No. of tests ', T);
let i = 0;
while (t++ < T) {
readInput(
t,
inp[++i]
// inp[++i], // <------------ NOTE: Enable more lines based on more lines of input
// inp[++i]
);
}
}
let readInput = (t, S1) => {
let [L, R] = S1.split(' ').map((x) => x);
return run(t, L, R);
};
let run = (t, L, R) => {
// console.log('t', t, ' L', L, 'R', R);
L = +L;
R = +R;
let output = 'Case #' + t + ': ' + (L + R);
console.log(output);
return output;
};
export { readInput, run };
Input:
3
1 2
3 4
Output:
Case #1: 3
Case #2: 7
Mar 2021 and I'm using this simple structure to get start.
function getArgs() {
const [N, K, P] = stdin.nextNums();
const S = Array(N).fill().map(() => stdin.nextNums());
return [S, P]; // Array of inputs [stacks, plates]
}
function solve(stacks, plates) {
stdout.log(stacks, plates);
}
input = `
Put your test input here
`;
((c,f,g,t)=>(r=>(g.stdin={nextWord:()=>r[c++],nextNum:()=>+r[c++
],nextWords:()=>r[c++].split` `,nextNums:()=>r[c++].split` `.map
(e=>+e)},g.stdout={log(...d){t.log(d.map(e=>typeof e==='object'?
JSON.stringify(e):e).join` `)}},[...Array(stdin.nextNum())].map(
(_,i)=>t.log(`Case #${1+i}: ${solve(...getArgs())}`))))((g.input
||f.readFileSync(0,'utf-8')).trim().split`\n`))(0,require('fs'),
globalThis,console);
Use input when running in Node cmd, set input to empty string to read from stdin on the competition site.
stdin provides 4 functions nextWord(s), nextNum(s) for handling next line whether it is a string, a number or an array of either.
stdout provides function log in case you want to print out objects in the terminal.
Design your method of reading stdin in getArgs. Note that the function only handles arguments for one single test case only.
Design your solution in solve.

Encoding info_hash for request torrent tracker

I would like to interact with a torrent tracker in Node.js.
So I calculate the info_hash and I get the good one :
7cd350e5a70f0a61593e636543f9fc670ffa8a4d
But to be send to the tracker it have to be urlencoded. The problem is I don't know how to do it to get the right encoded value which is
%7c%d3P%e5%a7%0f%0aaY%3eceC%f9%fcg%0f%fa%8aM
I doesn't know how to get this value, because it doesn't follow the %nnformat
I tried with encodeURIComponent and querystring, but I didn't get the right string.
Here's one solution that matches the expected output (including capitalization):
var h = '7cd350e5a70f0a61593e636543f9fc670ffa8a4d';
h = h.replace(/.{2}/g, function(m) {
var v = parseInt(m, 16);
if (v <= 127) {
m = encodeURIComponent(String.fromCharCode(v));
if (m[0] === '%')
m = m.toLowerCase();
} else
m = '%' + m;
return m;
});
console.log(h);
// outputs: %7c%d3P%e5%a7%0f%0aaY%3eceC%f9%fcg%0f%fa%8aM

Resources