Search

Results include substring matches and semantically similar vals. Learn more
pdebie avatar
youtubeFeed
@pdebie
Fetches all videos for a Youtube channel, by using its RSS feed.
Script
import { fetchRss } from "https://esm.town/v/pdebie/fetchRss";
export function youtubeFeed(channelId: string, lastCheckDate?: string | number) {
return fetchRss(
"https://www.youtube.com/feeds/videos.xml?channel_id=" + channelId,
seflless avatar
getRotationFromFFmpegDisplayMatrixString
@seflless
An interactive, runnable TypeScript val by seflless
Script
export function getRotationFromFFmpegDisplayMatrixString(str: string) {
let elements = str.match(/[-+]?\d+/g)?.map(Number) || [];
if (elements.length !== 9) {
faekiva avatar
rssToBsky
@faekiva
[disc five] RssToBsky - This val periodically polls the "[disc five]" rss feed and posts the new items to bsky.
Cron
const rssFeeds: RSSEntry[] = [
["[disc five]", "https://www.discfive.com/rss/"],
export async function pollRSSFeeds({ lastRunAt }: Interval) {
const password = Deno.env.get(bskyPassword);
if (!password) {
jamiedubs avatar
mediainfo
@jamiedubs
Fetches details about media files (images, video, audio, etc) using Mediainfo.js . e.g. codecs used, duration, file sizes, ID3 tags, etc. Check out https://jamiedubs-mediainfo.web.val.run/ for a little interactive example Beta! API response not 100% stable, I might move things around still. Especially this summary field idea
HTTP
frameRate?: string;
function App({ initialUrl }: AppProps) {
// Bob Dylan mp3 found in an open directory
</html>
function summarizeMediaInfo(mediaInfo: MediaInfoResult): MediaSummary {
const summary: MediaSummary = {
return summary;
export default async function server(request: Request): Promise<Response> {
const url = new URL(request.url);
kognise avatar
stopDeparturesSingleCountdown
@kognise
An interactive, runnable TypeScript val by kognise
Script
import { stopDepartures } from "https://esm.town/v/kognise/stopDepartures";
export async function stopDeparturesSingleCountdown(globalStopId: string) {
const res = await stopDepartures(globalStopId);
return res[0].itineraries[0].schedule_items.map((bus) => ({
rektdeckard avatar
FontPack
@rektdeckard
An interactive, runnable TypeScript val by rektdeckard
HTTP
let next = PUA_START;
function nextAvailableCodePoint() {
while (usedCodes.has(next)) {
inline?: boolean;
function bufferToBase64(buffer: ArrayBuffer) {
let binary = "";
return btoa(binary);
export default async function(
req: Request,
stevekrouse avatar
rimeRateLimitExceeded
@stevekrouse
An interactive, runnable TypeScript val by stevekrouse
Script
import { email } from "https://esm.town/v/std/email?v=9";
import { rimeUsage } from "https://esm.town/v/stevekrouse/rimeUsage";
export function rimeRateLimitExceeded(text: string) {
rimeUsage[new Date().toLocaleDateString()] =
(rimeUsage[new Date().toLocaleDateString()] || 0) +
tmcw avatar
sort
@tmcw
An interactive, runnable TypeScript val by tmcw
Script
import { Material } from "https://esm.town/v/tmcw/Material";
import { Recipe } from "https://esm.town/v/tmcw/Recipe";
export function sort(recipes: Map<string, Recipe>, materials: Material[]) {
const have = new Set(materials.map((m) => m.name));
return Array.from(recipes.values()).map((recipe) => {
rwev avatar
polygonTickerSummary
@rwev
An interactive, runnable TypeScript val by rwev
Script
import { msDay } from "https://esm.town/v/stevekrouse/msDay?v=1";
import { polygonPriceOnWeekdayMsAgo } from "https://esm.town/v/rwev/polygonPriceOnWeekdayMsAgo";
export async function polygonTickerSummary(ticker) {
return {
day: await polygonPriceOnWeekdayMsAgo(
rwev avatar
polygonPriceOnWeekdayMsAgo
@rwev
An interactive, runnable TypeScript val by rwev
Script
import { adjustMsToWeekday } from "https://esm.town/v/rwev/adjustMsToWeekday";
import { msToIsoDate } from "https://esm.town/v/rwev/msToIsoDate";
export async function polygonPriceOnWeekdayMsAgo(ticker, msAgo) {
const date = msToIsoDate(
adjustMsToWeekday(Date.now() - msAgo)
vladimyr avatar
libmultibase
@vladimyr
// SPDX-License-Identifier: 0BSD
Script
0xeb51: KeyType["JWK-JCS"],
} as const;
export function multibaseToBytes(input: string) {
const bytes = base58btc.decode(input);
const [codec, prefixLength] = varint.decode(bytes);
throw new Error("error: invalid key size");
return { keyBytes, keyType: keyType.name };
export function bytesToMultibase(keyBytes: Uint8Array, keyType: string) {
const type = KeyType[keyType];
if (!type) throw new TypeError("error: unsupported key type");
stevekrouse avatar
newStripeSubscriber
@stevekrouse
New Stripe Subscription Handler This val processes new Stripe subscribers to Val Town Pro. It sends our team a Discord notifcation. It takes a couple of steps if you'd like to set up something similar for your own Stripe account. Setup Fork this HTTP val Create a new webhook in Stripe Add your val's HTTP endpoint URL into the Stripe webhook Select customer.subscription.updated as the only event to listen to (more on this below) Add your stripe_sk_customer_readonly to your Val Town Env Variables Add your webhook's signing secret as STRIPE_WEBHOOK_SECRET to you Val Town Env Variables How the code is structured Verifies the signature to make sure it's really from Stripe Filters out only newly created subscriptions Sends off the Discord message & email Which Stripe event type to listen to Stripe sends webhooks for many different kinds of events. Relevant for us here are: customer.subscription.created (what we used to listen for) customer.subscription.updated (what we're currently listening for) The issue with customer.subscription.created is that it triggers too early, before the user's payment actually goes through. This is a problem because in early Nov 2024 we started getting credit card fraudsters testing cards using our service. We started getting lots of notifications for new subscriptions that never actually became active. Note: if anyone knows good ways to prevent that sort of behavior at the root, please let me know by commenting on this val! In order to only get notified on a valid subscription, we now subscribe to customer.subscription.updated . This event happens on any subscription change, including renewals and cancellations, so now we have to filter those events to only get new subscriptions, ie where: event.data.previous_attributes.status === 'incomplete' && event.data.object.status === 'active'
HTTP
const stripe = new Stripe(Deno.env.get("stripe_sk_customer_readonly") as string, {
apiVersion: "2020-08-27",
function getStripeCustomer(customerId: string) {
return stripe.customers.retrieve(customerId);
export let newStripeEvent = async (req: Request) => {
emarref avatar
HttpMiddlewareChain
@emarref
An implementation of chainable http middleware. Wrap your http handlers in middleware to add functionality and reduce code duplication. export default function chain() .then(add(requireHttpMethod("post"))) .then(add(requireAuthentication())) .then(send())
Script
An implementation of chainable http middleware.
Wrap your http handlers in middleware to add functionality and reduce code duplication.
```ts
export default function chain()
.then(add(requireHttpMethod("post")))
send: (response: Response) => HttpHandler;
export function chain(): Promise<Chain> {
const stack = new Set<HttpMiddleware>();
async function getChain(): Promise<Chain> {
return { add, send };
let happy = true;
async function loop() {
middleware = queue.shift();
return getChain();
export function add(middleware: HttpMiddleware) {
return function({ add }: Chain) {
return add(middleware);
export function send(response?: Response) {
return function({ send }: Chain) {
return send(response ?? success());
dotim avatar
checkTomorrowTempoEdf
@dotim
Ce script permet de vérifier le statut du jour Tempo de demain (Bleu, Blanc ou Rouge) en utilisant l'API publique Couleur Tempo. S'il détecte un jour Rouge ou blanc, il envoie une notification pour avertir l'utilisateur. En cas d'erreur lors de la récupération des données, une notification d'erreur est également envoyée.
Cron
day: "numeric",
} as const,
export default async function checkTempoStatus() {
try {
const tempoData = await TempoService.getTomorrow();
ktodaz avatar
sendDiscordMessage
@ktodaz
Send Chunked Discord Message This function is used to send a message to a Discord webhook. If the message exceeds the maximum character limit (2000 characters), it will be divided into chunks and sent as multiple messages. Parameters message (string): The message to be sent to the Discord webhook. Return Value This function does not return any value. Example Usage: const message = "This is a long message that needs to be sent to Discord. It may exceed the character limit, so it will be divided into smaller chunks."; await @ktodaz.sendDiscordMessage(message); In the above example, the sendDiscordMessage function is used to send the message to the Discord webhook. If the message exceeds 2000 characters, it will be split into smaller chunks and sent as separate messages. Required Secrets: Ensure you have added the secret for discord_webhook in your val.town secrets settings (Profile -> Secrets)
Script
# Send Chunked Discord Message
This function is used to send a message to a Discord webhook. If the message exceeds the maximum character limit (2000 charac
### Parameters
### Return Value
This function does not return any value.
## Example Usage:
await @ktodaz.sendDiscordMessage(message);
In the above example, the sendDiscordMessage function is used to send the message to the Discord webhook. If the message exce
## Required Secrets:
webhook_url: string = process.env.discord_webhook,
function chunkString(str) {
const chunks = [];