Every team I've been on has a #general channel that goes quiet by 11am. A daily digest bot is the easiest way to give people a reason to glance back at it, and "today's tech headlines" is a low-effort, high-signal thing to post that nobody minds seeing every morning.
Here's a complete Slack/Discord bot, built on NewTqnia's free public news API, that you can have running in about 15 minutes.
What we're building
A small Node script that:
- Fetches today's tech headlines (AI, robotics, space, health, energy, and a few other beats) from NewTqnia's API
- Formats them as a Slack or Discord message
- Posts them to a webhook
- Runs on a schedule via GitHub Actions, so there's no server to keep alive
Step 1: Install the SDK
npm install newtqnia-node
It's a small, typed client around NewTqnia's REST API. No API key is required for this.
Step 2: The script
// digest-bot.mjs
import { NewTqniaClient } from "newtqnia-node";
const client = new NewTqniaClient({ application: "tech-news-digest-bot" });
async function getDigest(locale = "en", limit = 5) {
// "today" can be empty early in the day (it resets on the Asia/Dubai
// boundary), so fall back to "latest" if there's nothing yet.
const today = await client.news.today({ locale, limit });
if (today.articles.length) return today;
return client.news.latest({ locale, limit });
}
function toDiscordPayload(digest) {
const lines = digest.articles
.map((a) => `• [${a.title}](${a.url})`)
.join("\n");
return {
content: `**Today in Tech, via [NewTqnia](${digest.publisher.url})**\n\n${lines}`,
};
}
function toSlackPayload(digest) {
const lines = digest.articles
.map((a) => `• <${a.url}|${a.title}>`)
.join("\n");
return {
text: `*Today in Tech, via <${digest.publisher.url}|NewTqnia>*\n\n${lines}`,
};
}
async function postDigest(webhookUrl, kind = "discord", locale = "en") {
const digest = await getDigest(locale);
const payload = kind === "slack" ? toSlackPayload(digest) : toDiscordPayload(digest);
const res = await fetch(webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (!res.ok) {
throw new Error(`Webhook post failed: ${res.status} ${await res.text()}`);
}
}
const webhookUrl = process.env.DISCORD_WEBHOOK_URL || process.env.SLACK_WEBHOOK_URL;
const kind = process.env.SLACK_WEBHOOK_URL ? "slack" : "discord";
if (!webhookUrl) {
console.error("Set DISCORD_WEBHOOK_URL or SLACK_WEBHOOK_URL");
process.exit(1);
}
postDigest(webhookUrl, kind).catch((err) => {
console.error(err);
process.exit(1);
});
A couple of things worth calling out:
-
todayvslatest: NewTqnia'stodayendpoint resets on the Asia/Dubai day boundary and can legitimately return an empty array if nothing's published yet when your job runs. Falling back tolatestavoids posting an empty digest. - Attribution: NewTqnia's API terms ask that you keep a visible link back when you display its content and preserve the article URLs it returns. Both formatters above do that by linking the publisher name, so you get this right by default rather than as an afterthought.
Step 3: Get a webhook URL
Discord: Server Settings > Integrations > Webhooks > New Webhook > copy the URL.
Slack: create an Incoming Webhook for the channel you want and copy the URL it gives you.
Test locally:
DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/..." node digest-bot.mjs
Step 4: Run it on a schedule, for free
No server needed. A scheduled GitHub Action does the job:
# .github/workflows/digest.yml
name: Post daily tech digest
on:
schedule:
- cron: "0 6 * * *" # 06:00 UTC daily, adjust to your team's morning
workflow_dispatch: {}
jobs:
post:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm install newtqnia-node
- run: node digest-bot.mjs
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
Add DISCORD_WEBHOOK_URL (or SLACK_WEBHOOK_URL) as a repo secret, push, and you're done. workflow_dispatch is there so you can trigger a test run by hand from the Actions tab instead of waiting for the cron.
Where to take it from here
- Filter by beat with
category(e.g.client.news.today({ category: "artificial-intelligence" })) if your team only wants AI news, not space and health too. - Post in Arabic by passing
locale: "ar". The API and SDK are bilingual by default. - If you'd rather not run a script at all, NewTqnia also has a drop-in embeddable widget for putting the same feed directly on a webpage instead of a chat channel.
Full API reference and the PHP SDK are on the developer page if you want to build something more involved than a digest bot.
Top comments (0)