Last active
March 27, 2022 05:07
-
-
Save darvesh/bc053a506f09e24c23b6eb45080252b8 to your computer and use it in GitHub Desktop.
A Telegram bot that sends notification when there is new blog post on https://devblogs.microsoft.com/typescript/feed/. You can also list blogs inline and send.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Bot } from "https://deno.land/x/[email protected]/mod.ts"; | |
import { parseFeed, Feed } from "https://deno.land/x/[email protected]/mod.ts"; | |
const INTERVAL = 60 * 10 * 1000; | |
const RECIPIENT = -1001335202320; | |
const TOKEN = Deno.args[0]; | |
if (!TOKEN) throw new Error("Please provide a bot token"); | |
const bot = new Bot(TOKEN); | |
type ArrayElement<T extends unknown[]> = T extends (infer E)[] ? E : never; | |
type FeedEntry = ArrayElement<Feed["entries"]>; | |
type Post = { title: string; published: Date; link: string }; | |
type Maybe<T> = T | null; | |
const escapables = { | |
"<": "<", | |
">": ">", | |
"&": "&", | |
"'": "'", | |
'"': """, | |
}; | |
const escape = (s: string) => | |
s.replace(/<|>|&|"|'/g, (r) => escapables[r as keyof typeof escapables] || r); | |
async function getPosts(attempt = 0): Promise<FeedEntry[]> { | |
try { | |
if (attempt > 2) throw new Error("Retry attempt count exceeded"); | |
const response = await fetch( | |
"https://devblogs.microsoft.com/typescript/feed/" | |
); | |
const xml = await response.text(); | |
const feed = await parseFeed(xml); | |
return feed.entries; | |
} catch { | |
await new Promise((res) => setTimeout(res, 60 * 1000)); | |
return getPosts(attempt + 1); | |
} | |
} | |
const parser = (feed: FeedEntry): Maybe<Post> => { | |
const published = feed.published; | |
const title = feed.title?.value; | |
const link = feed.links[0].href; | |
if (title && published && link) return { title, published, link }; | |
return null; | |
}; | |
const filterNewPosts = (post: Maybe<Post>) => | |
post && | |
post.published.getTime() > new Date("2022-02-09T18:45:19.000Z").getTime() | |
? post | |
: null; | |
const sendPost = (bot: Bot, post: Maybe<Post>) => | |
post && | |
bot.api.sendMessage( | |
RECIPIENT, | |
`<b>New TypeScript blog post published ๐</b>\n\n<a href="${ | |
post.link | |
}">${escape(post.title)}</a>`, | |
{ | |
parse_mode: "HTML", | |
} | |
); | |
setInterval(async () => { | |
try { | |
const posts = await getPosts(); | |
await Promise.all( | |
posts | |
.map(parser) | |
.filter(filterNewPosts) | |
.map((post) => sendPost(bot, post)) | |
); | |
} catch (error) { | |
console.trace(error); | |
} | |
}, INTERVAL); | |
bot.command("start", (ctx) => ctx.reply("Welcome! Up and running.\nDev: https://github.com/darvesh")); | |
bot.on("inline_query", async (ctx) => { | |
const posts = await getPosts(); | |
await ctx.answerInlineQuery( | |
posts | |
.map((post) => { | |
const p = parser(post); | |
return p | |
? { | |
type: "article" as const, | |
title: p.title, | |
id: Math.random().toString(), | |
input_message_content: { | |
message_text: `<b>New TypeScript blog post published ๐</b>\n\n<a href="${ | |
p.link | |
}">${escape(p.title)}</a>`, | |
parse_mode: "HTML" as const, | |
}, | |
} | |
: null; | |
}) | |
.filter(<T>(res: T): res is NonNullable<T> => Boolean(res)) | |
); | |
}); | |
bot.catch(console.trace); | |
bot.start({ | |
onStart: () => console.log("Bot started"), | |
drop_pending_updates: true, | |
}); |
Author
darvesh
commented
Mar 27, 2022
•
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment