Skip to content

Instantly share code, notes, and snippets.

@creesch
Created September 29, 2017 09:43
Show Gist options
  • Save creesch/93257450cdd70994019329c5af27efb9 to your computer and use it in GitHub Desktop.
Save creesch/93257450cdd70994019329c5af27efb9 to your computer and use it in GitHub Desktop.
Modmail discord feed
global.configuration = {
DEBUG: true,
modmailChannelWebhook: 'webhook url goes here',
// States go here, colors are hex colors with # replaced with 0x
modmailStates: [
{
'state': 'mod',
'color': '0x46d160',
'subreddits': 'history'
},{
'state': 'new',
'color': '0x0dd3bb',
'subreddits': 'history'
},{
'state': 'highlighted',
'color': '0xffb000',
'subreddits': 'history'
},{
'state': 'notifications',
'color': '0x24a0ed',
'subreddits': 'history'
}
],
redditConfig: {
userAgent: '/u/FILLINYOURNAMEHERE discord modmail feed',
oauth: {
type: 'script',
key: 'YOURKEY',
secret: 'YOURSECRET',
username: 'USERNAME',
password: 'PASSWORD',
// make sure to set all the scopes you need. We are lazy so we do ALLTHESCOPES
scope: ['account', 'creddits', 'edit', 'flair', 'history', 'identity', 'livemanage', 'modconfig', 'modcontributors', 'modflair', 'modlog', 'modmail', 'modothers', 'modposts', 'modself', 'modwiki', 'mysubreddits', 'privatemessages', 'read', 'report', 'save', 'submit', 'subscribe', 'vote', 'wikiedit', 'wikiread']
}
}
};
// Configuration goes here.
require('./config.js');
if (!configuration.DEBUG) {
console.log = function() {};
}
const fs = require('fs');
const axios = require('axios');
const snooCore = require('snoocore');
const reddit = new snooCore(configuration.redditConfig);
// Object where we store the ids of conversations we have already seen. Is filled on restart from a json file on disk.
let modmailChecked = {};
// New modmail is a bit weird in the structure. There is a list of authors where one of them has "isOp" set to true.
// this is a quick and dirty method to give us the name of OP when we feed the function the authors object.
function getOp(authorObject) {
let opName;
for (let key in authorObject) {
if (authorObject.hasOwnProperty(key)) {
const currentAuthor = authorObject[key];
// isOp is true, we have a name.
if (currentAuthor.isOp) {
opName = currentAuthor.name;
}
}
}
// As I said, quick and dirty we simply assume we have the name now and return it.
return opName;
}
// reddit related errrors, for debugging and such.
reddit.on('error', function(error) {
console.error('REDDIT ERROR:');
console.error(error);
});
reddit.on('ENOTFOUND', function(error) {
console.error('REDDIT ERROR:');
console.error(error);
});
// The "heavy" lifting happens here. Gets the conversations for the subreddits and state given.
// Then if there are new things it outputs those to the discord webhook with the color given.
function getConversations(subreddits, state, color) {
// Call the api
reddit('/api/mod/conversations').get({
sort: 'recent',
state: state,
entity: subreddits
}).then(function(result) {
// We are interested in the conversations.
const conversations = result.conversations;
// In order to avoid discord rate limits we store new conversations in this array and send them all at once with one request once done.
let embeds = [];
// Itterate over the conversations.
for (let key in conversations) {
if (conversations.hasOwnProperty(key)) {
// If we haven't checked it before we can process it.
if (!modmailChecked[state].includes(key)) {
const currentConversation = conversations[key];
// Lets determine the name we want to attach to the embed.
const opName = getOp(currentConversation.authors)
// Here we build up the message preview. For now we simply show the raw markdown.
const messagePreviewID = currentConversation.objIds[0].id;
const messagePreview = result.messages[messagePreviewID].bodyMarkdown;
// Build up and push the embed object to the embeds array.
embeds.push({
title: currentConversation.subject,
url: `https://mod.reddit.com/mail/mod/${key}`,
author: {
name: `${state} - modmail from /u/${opName}`,
url: `https://reddit.com/user/${opName}`
},
description: messagePreview,
color: parseInt(color)
});
// Let's avoid that our history array gets annoyingly long.
if (modmailChecked[state].length > 100) {
modmailChecked[state].shift();
}
modmailChecked[state].push(key);
}
}
}
// Fire the webhook
if (embeds.length > 0) {
axios.post(configuration.modmailChannelWebhook, {
embeds: embeds
});
}
}).catch(function(error) {
console.log('reddit error:');
console.log(error);
});
}
// This function goes over the configuration.modmailStates array and is also responsible for looping and storing the modmailChecked in a local json file.
function getModmail() {
fs.writeFile('modmailChecked.json', JSON.stringify(modmailChecked), 'utf8', function(err, data) {
if (err) {
throw err;
}
});
// Go over each state and fire the getConversations function.
configuration.modmailStates.forEach(function(state) {
getConversations(state.subreddits, state.state, state.color)
});
// check for new modmail every twenty seconds.
setTimeout(getModmail, 20000);
}
// This check really not needed, but maybe someone else wants to use this in the future and this makes it easier to set up.
// Hello future user!
// This checks for the excistence of the "modmailChecked.json" file and fills the "modmailChecked" object with the contents.
if (fs.existsSync('modmailChecked.json')) {
fs.readFile('modmailChecked.json', 'utf8', function(err, data) {
if (err) {
throw err;
}
modmailChecked = JSON.parse(data);
getModmail();
});
// This makes the "modmailChecked.json" file if it doesn't exists yet and builds the modmailChecked object.
} else {
configuration.modmailStates.forEach(function(state) {
modmailChecked[state.state] = [];
});
fs.writeFile('modmailChecked.json', JSON.stringify(modmailChecked), 'utf8', function(err, data) {
if (err) {
throw err;
}
getModmail();
});
}
// Let's gracefully exit when needed.
process.on('SIGINT', function() {
console.log('\nGracefully shutting down from SIGINT (Ctrl-C)');
fs.writeFile('modmailChecked.json', JSON.stringify(modmailChecked), 'utf8', function(err, data) {
if (err) {
throw err;
}
process.exit();
});
});
{
"name": "modmailDiscordFeed",
"version": "0.1.0",
"description": "Checks modmail and outputs new conversation to a discord channel",
"main": "modmailbot.js",
"scripts": {
"start": "node modmailbot.js"
},
"keywords": [
"discord",
"bot",
"modmail",
"reddit"
],
"author": "creesch",
"license": "MIT",
"dependencies": {
"axios": "^0.16.2",
"snoocore": "^3.3.1"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment