I’m calling it automator, it fulfills its destiny by automatically answering all domain removal topics that it receives from start. It will also include keywords that can be searched to find domain-removal topics tagged by automator, it’s almost done
The code below is what used to do everything.
const fs = require("fs");
const crypto = require("crypto");
const nodemailer = require("nodemailer");
const transport = nodemailer.createTransport({
host: "smtp.zoho.eu",
port: 465,
secure: true,
auth: {
user: "redacted",
pass: "redacted"
}
});
const createMessage = from => `Hello ${from}!
Welcome (back?) to Glitch!
For domain removals please send an email to [email protected] so they can start processing your domain removal request.
I totally understand that this might be frustrating, but this is the fastest way to get your custom domain removed.
I hope your problem get solved quickly!
Thanks,
Automator.
> **Note**: This is an automated message.
-- ignore this --
keyword-tag-domain-removal`;
const sendMessage = (user, id, from) => console.log("sending", user, id, from) || transport.sendMail({
from: "'Spam', [email protected]",
to: user,
inReplyTo: id,
text: createMessage(from)
});
const containsDomainRemoval = t => {
t = t.trim().toLowerCase();
return t.includes("domain");
};
exports.register = function ()
{
this.register_hook("mail", "mail");
this.register_hook("rcpt", "mail");
this.register_hook("rcpt_ok", "mail");
this.register_hook("data", "mail");
this.register_hook("data_post", "mail");
this.loginfo("Hello world")
}
exports.mail = function (next, con)
{
if (con.hook === "mail")
{
con.transaction.parse_body = true;
}
if (con.hook === "data_post")
{
const user = con.transaction.mail_from.user + "@" + con.transaction.mail_from.host;
console.log(con.transaction.body.children[0].bodytext)
let msg = con.transaction.body.children[0].bodytext.match(/(.|\n)*(?=---)/g);
if (!msg) return next(OK);
msg = msg[0];
if (!msg) return next(OK);
msg = msg.trim();
if (msg.length > 2000) return next(OK);
let id = con.transaction.header.headers["message-id"];
let sub = con.transaction.header.headers["subject"];
if (!id) return next(OK);
if (!sub) return next(OK);
if (!id[0]) return next(OK);
if (!sub[0]) return next(OK);
id = id[0].substring(1, id[0].length - 2);
sub = sub[0].trim();
let inReplyTo = con.transaction.header.headers["in-reply-to"];
inReplyTo = inReplyTo[0].substring(1, inReplyTo[0].length - 2);
let from = con.transaction.header.headers["from"];
if (!from) return next(OK);
from = from[0];
if (from.includes(" via Glitch Support <[email protected]>\n")) from = from.substring(0, from.length - 48);
console.log(user, inReplyTo, id);
console.log(from);
console.log(sub);
console.log(msg);
if (containsDomainRemoval(sub) || containsDomainRemoval(msg))
sendMessage(user, id, from);
}
next(OK);
}
This is a really cool - I was gonna do it with the API, but this is a great way to do it without a token! Can’t wait to see it in action. I wonder how long it will take from seeing a topic and then replying
Disclaimer: if you’re using a Gmail account for Nodemailer, you will have to use Gmail oAuth and simply “allowing less secure apps” for your Gmail account will not work. Pro: no need to enable less secure apps, better security, and no need to enter your password in the pass field (the oAuth token is enough). Cons: too complicated.
I’ll be doing a remake of this using puppeteer, because I realize now that the Haraka mail server is over 9 years old, and hasn’t a had a commit in their repo in 5 years.
ahh i got u. btw delete email sonce processed I run into that a lot.if all doesn’t work, just store processed emails in a DB as you get em and cross check u haven’t seen it before
That’s not the problem, I have written a short script to test that theory but that doesn’t work, it seems to receive a lot of emails still, old connections are literally opened to send me old emails. I will use a similar method on the new system, but that won’t be using silly emails. xd
I am making a new bot, because the email one didn’t work very well. And if it gets banned it would be hilarious, it’s not like the bot is gonna spam the entire forum. It has a very specific task to do.
Well, there is an API for that, but I don’t have any api keys so I can’t do it for Glitch. I’m however using puppeteer to interact with support.glitch.com website.
Hi there @ihack2712!
^^^
That’s what I do, so the Hello {from}! could be Hello @{from}! so it pings them like I do. This makes sure that it sends them an email or whatever and makes sure they get a notification from your post. I think it’s good practise to tag the user
Eddie
The bot can now fetch topics with their messages and the OP. It caches which topics has been dealt with, the only thing left is to actually answer the topics.
I’m not scraping anything, I’m using the Discourse API. And I send a request to the endpoint every 5 minutes, an average user will send a request to the forum every 5-15 seconds.
As long as the device is online and the browser is running in the background it actually will send a request every 5-15 seconds. And for an average user, this is true.
Ya um I don’t think anybody is on the forum/has a forum tab open for more than a few hours a day. But discourse supports sending msgs via the api w/o an api key?
If you don’t understand how this works you can lookup some recent tutorials about service workers, they run as a thread on your device, independent whether or not you have tabs or windows open.
No they do not, I only use Puppeteer for sending messages. Everything else is fetched through the API.
const t = topics[i];
const postID = (await api.getTopic(t.id)).post_stream.stream[0];
const post = await api.getPost(postID);
t.content = post.raw;
t.username = post.username;
Here’s the JSON output of what the final topic object looks like:
{
"id": 29098,
"title": "Test domain removal post",
"slug": "test-domain-removal-post",
"created_at": "2020-07-24T15:05:28.137Z",
"content": "Please ignore this post, this is my final testing post for checking whether or not Automator is going to work, sorry again.",
"username": "ihack2712"
},
Just a quick disclaimer!!
I DO NOT STORE ANY INFORMATION EXCEPT FOR A TOPIC ID TO PREVENT THE BOT FROM RE-POSTING OR TO MAKE A REQUEST AGAINST THE SAME TOPIC TWICE.
Takes about a minute to post Let’s hope it beats any Regulars! Works great!
Does it look for ‘domain’ or ‘domain removals’? As some posts are asking how to add them. Maybe add some more context to the post, like:
If you want to add a domain, bla, bla, bla.
If you want to remove a domain, bla, bla, bla.
If this automated message does not relate to your topic, please kindly ignore the post
The reason it takes time is because I only fetch updates from discourse every 5 minutes, I could fetch them per minute as well. That will still not be as bad as a normal discourse client.
I’m gonna have to take off my 2fa for github now, because I’ll be using that account for a day or two on the bot, I need to start the bot browser in headless mode, because it is really annoying that my mac keeps opening the chromium window It’ll be up in a couple of minutes.
@jenn as our awesome Glitch Community Engineer, can I request approval of this bot, as this will really help the community save time sending posts about custom domains
The bot is now up and running, I’ll keep it online until I get an explicit message from the moderation or administration to turn it off. (Current version is 216 lines of javascript code that’s not bad).
The bot is guaranteed to answer domain removal posts within 1-2 minutes.
I’m also going to implement features:
deleting an automated message (can be used by administration and/or moderation, and selected moderators).
Changing the topic title to [domain-removal] {title} when the topic is confirmed to be related to domain removals.
Adding a feedback page to the bot.
Adding an API to the bot.
Happy Glitching!
Edit: The bot will operate under my account for 1-2 days before I switch it to @automator.
Edit 2: Everything seems to be stable and working:
Message to Glitch: If you want me to take this down I will do so ASAP. Otherwise, I’d ask that we can see how this bot goes for some time, and discuss possibly giving me an api key to make the bot a less load for the discourse server.
P.S. I really want to work closely with Glitch regarding this bot to make sure that both Glitch and the community feels that the bot is trusting and welcoming.
Hi there @ihack2712!
Maybe you should set up Puppeteer to read posts too and more interaction so the bot can get Regular on the forum and can rename, etc. Would love for it to be a moderator so that it can look official/like it’s a bot.
That would be very difficult, which is why I’m hoping Glitch will give me an API key with permissions to do some stuff like a regular can, that way it can look more official and more welcoming.
If I get and API key it will be A LOT easier to continue developing the bot, and I will actually take the time to write clean code with pipelines and so on, so everything can be open-source and look beautiful.
But honestly, this is probably something Glitch has to discuss internally whether or not to allow a single forum member to run an unofficial bot and not letting anyone else do the same. I will understand if Glitch denies my request.
The files doesn’t contain any data, it is empty, the file is simply just there to indicate that the topic has been dealt with, or basically telling the program not to interact with that topic ever again.
Here’s the code that uses these files:
api.fs = {};
if (!Fs.existsSync("/topics_")) Fs.mkdirSync("/topics_", {recursive: true});
api.fs.add = id => Fs.writeFileSync("/topics_/" + id, "");
api.fs.has = id => Fs.existsSync("/topics_/" + id);
const do_a_loop = async () => {
const topics = (await api.getLatestTopics()).filter(topic => !api.fs.has(topic.id));
for (let i = 0; i < topics.length; i++)
{
const t = topics[i];
const postID = (await api.getTopic(t.id)).post_stream.stream[0];
const post = await api.getPost(postID);
t.content = post.raw;
t.username = post.username;
if (topicContainsDomain(t) && !api.fs.has(t.id))
{
try
{
await sendDomainMessage(browser, page, t);
api.fs.add(t.id);
} catch (error)
{
console.log("Failed to deliver domain removal message to %s on topic %s", t.username, t.title);
console.log(error);
console.log();
}
} else
{
api.fs.add(t.id);
}
}
};
This is the old overpopulated thread, I could probably delete it. I made a second thread which will sorta be a Wiki for Automator, better explaining what it does. This thread was rather meant for discussion regarding how things should be done