lambda.js (185 lines of code) (raw):
const { BskyAgent, AppBskyFeedPost } = require("@atproto/api");
const emailer = require("./mods/emailer");
const cheerio = require("cheerio");
const Parser = require("rss-parser");
const parser = new Parser({
customFields: {
item: [
['media:content', 'media:content', {keepArray: true}],
]
}
})
const dotenv = require('dotenv');
dotenv.config()
let accounty = process.env.ACC
let passy = process.env.PASS
let lastPost = 1200000
const numberOfPosts = 1 // Number of posts at one time
const aus_feeds = [
'https://www.theguardian.com/tracking/commissioningdesk/australia-news/rss',
'https://www.theguardian.com/tracking/commissioningdesk/australia-state-news/rss',
'https://www.theguardian.com/tracking/commissioningdesk/australia-culture/rss',
'https://www.theguardian.com/tracking/commissioningdesk/australia-lifestyle/rss',
'https://www.theguardian.com/tracking/commissioningdesk/australia-opinion/rss',
'https://www.theguardian.com/tracking/commissioningdesk/australia-politics/rss',
'https://www.theguardian.com/tracking/commissioningdesk/australia-sport/rss',
'https://www.theguardian.com/tracking/commissioningdesk/australia-features/rss',
'https://www.theguardian.com/tracking/commissioningdesk/australia-investigations/rss',
'https://www.theguardian.com/tracking/commissioningdesk/australia-data/rss',
'https://www.theguardian.com/tracking/commissioningdesk/australia-video/rss',
'https://www.theguardian.com/tracking/commissioningdesk/australia-pictures-/rss',
'https://www.theguardian.com/tracking/commissioningdesk/australia-technology/rss',
'https://www.theguardian.com/tracking/commissioningdesk/new-zealand/rss',
'https://www.theguardian.com/tracking/commissioningdesk/pacific-news/rss',
'https://www.theguardian.com/tracking/commissioningdesk/australia-business/rss',
'https://www.theguardian.com/tracking/commissioningdesk/the-rural-network/rss',
'https://www.theguardian.com/collection/5d60fb3d-9bb2-439b-81d4-3bd4d625165a/rss'
]
const global_feeds = [
'https://www.theguardian.com/collection/cdad59a3-e992-40f1-bf8d-677398064116/rss',
'https://www.theguardian.com/collection/016d967f-0303-4a47-b5e0-bf6d36ad4a52/rss',
'https://www.theguardian.com/collection/a63f-82a9-8f63-edf1/rss'
]
const post = async(agent, item) => {
const dom = await fetch(item.link)
.then((response) => response.text())
.then((html) => cheerio.load(html));
let image_url = null;
const image_url_ = dom('head > meta[property="og:image"]');
if (image_url_) {
image_url = await image_url_.attr("content");
}
const buffer = await fetch(image_url)
.then((response) => response.arrayBuffer())
const image = await agent.uploadBlob(buffer, { encoding: "image/jpeg" });
let post = {
$type: "app.bsky.feed.post",
text: item.title,
createdAt: new Date().toISOString()
};
post["embed"] = {
external: {
uri: `${item.link}?CMP=aus_bsky`,
title: item.title,
description: item.description,
thumb: image.data.blob,
},
$type: "app.bsky.embed.external",
};
const res = AppBskyFeedPost.validateRecord(post);
if (res.success) {
await agent.post(post);
return 'Posting succeeded!';
} else {
return 'Post failed!';
}
}
async function app(feed1, feed2=[]){
let list_of_stories = []
let already_posted = []
let cursor = "";
// ### Login and validate
const agent = new BskyAgent({ service: "https://bsky.social" });
await agent.login({
identifier: accounty,
password: passy,
});
// ### Grab the already posted stories
let recent = await agent.getAuthorFeed({
actor: accounty,
limit: 50,
cursor: cursor,
})
for (const feed of recent.data.feed) {
already_posted.push(feed.post.record.embed.external.uri)
}
const listicle = recent.data.feed.map(d => d.post.indexedAt)
const latest = new Date(Math.max.apply(null, listicle.map((e) => new Date(e))))
const diffInMs = new Date().getTime() - latest.getTime();
if ( diffInMs > lastPost) { // lastPost The last post was more than five minutes ago
// Only post world stories between 1am and 6am, min of 5 minutes between posts all the time
const getStories = async (rssfeeds) => {
const feedlist = await Promise.allSettled(rssfeeds.map(url => parser.parseURL(url)))
const shortlist = feedlist.filter(d => d.status == 'fulfilled')
const rss = shortlist.map(d => d.value)
for await (const feed of rss) {
for (const item of feed.items) {
const age = new Date().getTime() - new Date(item.date).getTime();
if (age < 28800000) { // Less than eight hours ago
if (!already_posted.includes(item.link + '?CMP=aus_bsky') && !containsMatch(["ntwnfb", "nfbntw"], item.link)) {
list_of_stories.push({
title: item.title,
link: item.link,
description: item.contentSnippet,
published : new Date(item.date).getTime()
})
}
}
}
}
}
await getStories(feed1)
if (list_of_stories.length == 0 && feed2.length > 0) {
await getStories(feed2)
}
if (list_of_stories.length > 0) {
let posting = `<p>Number of stories: ${list_of_stories.length }</p>`
let ordered = list_of_stories.sort((a, b) => a.published - b.published);
for (var i = 0; i < ordered.length; i++) {
let message = await post(agent, ordered[i]);
posting += `<p>Posted: ${ordered[i].title}</p>`
if (i >= (numberOfPosts - 1)) {
break
}
}
return posting
} else {
return "<p>No stories to post</p>"
}
} else {
return `<p>The last post was less than ${millisecondsToMinutes(lastPost)} minutes ago</p>`
}
}
function millisecondsToMinutes(milliseconds) {
const minutes = milliseconds / (1000 * 60);
return minutes;
}
function containsMatch(arr, searchString) {
for (let item of arr) {
if (item.includes(searchString)) {
return true;
}
}
return false;
}
const temporal = (timestamp) => {
var str = timestamp.toString()
return str.split(",")[0]
}
async function wrapper() {
let sydneyTime = new Date()
let today = temporal(new Date().toLocaleString("en-US"))
let start = new Date(new Date(`${today} 0:00:00 AM`).toLocaleString("en-US")).getTime()
let end = new Date(new Date(`${today} 6:00:00 AM`).toLocaleString("en-US")).getTime()
let response = ""
if (sydneyTime.getTime() > start && sydneyTime.getTime() < end) {
lastPost = 2400000 // 40 minutes
response = `<p>It is between 1am and 6am in Australia right now. ${sydneyTime}. International feeds.</p>`
response += await app(global_feeds, [])
} else {
lastPost = 1200000 // 20 minutes
response = "<p>Australian feeds.</p> "
response += await app(aus_feeds, global_feeds)
}
return response
}
/*
exports.handler = async (event) => {
const response = await wrapper()
if (process.env.TESTING == "TRUE") {
let temp = await emailer('Bluesky@' + new Date() + " - V2", `${response}`, 'work@andyball.info')
}
return response
};
*/
;(async function () {
const response = await wrapper()
if (process.env.TESTING == "TRUE") {
let temp = await emailer('Bluesky@' + new Date() + " - V2", `${response}`, 'work@andyball.info')
}
console.log(response)
})();