api.js (153 lines of code) (raw):

const dotenv = require('dotenv'); dotenv.config() const emailer = require("./mods/emailer"); //const Guardian = require('guardian-js'); const { BskyAgent, AppBskyFeedPost } = require("@atproto/api"); const cheerio = require("cheerio"); let accounty = process.env.ACC let passy = process.env.PASS let lastPost = 1200000 const numberOfPosts = 1 // Number of posts at one time //const guardian = new Guardian.default(process.env.API, false); // let edition = await guardian.content.search('au'); 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"); } let description = "" let description_ = dom('head > meta[property="og:description"]'); if (description_) { description = await description_.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: 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 (feed) => { for await (const item of feed) { const age = new Date().getTime() - new Date(item.webPublicationDate).getTime(); if (age < 28800000) { // Less than eight hours ago if (!already_posted.includes(item.webUrl + '?CMP=aus_bsky') && !containsMatch(["ntwnfb", "nfbntw"], item.webUrl)) { list_of_stories.push({ title: item.webTitle, link: item.webUrl, published : new Date(item.webPublicationDate).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>` } } 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>` let global_feeds = await getArticles('australia-news') response += await app(global_feeds, []) } else { lastPost = 1200000 // 20 minutes let aus_feeds = await getArticles('australia-news') let global_feeds = await getArticles('world') response = "<p>Australian feeds.</p> " response += await app(aus_feeds, global_feeds) } return response } 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 getArticles(section='australia-news') { const baseUrl = `https://content.guardianapis.com/${section}`; const params = new URLSearchParams(); params.append('api-key', process.env.API); const url = `${baseUrl}?${params.toString()}`; let json = await fetch(url).then(d => d.json()) return json.response.results } ;(async () => { const response = await wrapper() if (process.env.TESTING == "TRUE") { let temp = await emailer('Bluesky@' + new Date() + " - from API", `${response}`, 'work@andyball.info') } console.log(response) })();