stevekrouse
mayor of val town
Likes
452
trackIphoneTradein
@tmcw
Cron
Track iPhone trade-in prices iPhone trade in values - to trade phones back to Apple themselves - fluctuate over time , and eventually they no longer accept certain kinds of phones. You can still trade in an iPhone 7 - released in 2016 , about 6 years ago as of this writing. But no longer does an iPhone 6 have value back to Apple. This tracks those values - it runs every month and records prices to iphoneTradeInValues , which I'll visualize once there's some data.
ntfy
@xkonti
Script
Allows to publish a ntfy notification using a fluent builder configuration. Usage example import { ntfy } from "https://esm.town/v/xkonti/ntfy";
await ntfy()
.toServer(Deno.env.get("ntfyServer"))
.asUser(Deno.env.get("ntfyUser"), Deno.env.get("ntfyPassword"))
.toTopic("testing")
.withMessage("Hello there!")
.withTitle("First test")
.withViewAction("My website", "https://xkonti.tech")
.withTags("package", "val-town")
.withPriority("high")
.publish(); ⚠️ For the notification to be sent it needs to be published ( publish function). Use helper Executes specified functions that can modify the notification. Can be used to streamline authentication, apply common operations, etc. import { ntfy } from "https://esm.town/v/xkonti/ntfy";
const toMyNtfyServer = (builder: ReturnType<typeof ntfy>) => {
builder
.toServer(Deno.env.get("ntfyServer"))
.asUser(Deno.env.get("ntfyUser"), Deno.env.get("ntfyPassword"));
};
await ntfy()
.use(toMyNtfyServer)
.toTopic('home-automation')
.withMessage('You left the front door open')
.publish(); You can pass it multiple functions. Functions toServer(url) - optional Specifies a server that the notification will be sent do. By default it's https://ntfy.sh . asUser(user, password) - optional Authenticates with the user and password. Please use ValTown's secrets for this. await ntfy()
.asUser('user123', '12345')
... usingToken(token) - optional Authenticates using the provided token. Please use ValTown's secrets for this. await ntfy()
.usingToken('some-token')
... toTopic(topic) - required Specifies which topic to publish the message to. await ntfy()
.toTopic('home-automation')
... withMessage(message, markdown) - required Specifies the main message of the notification. You can also flag it as markdown by passing true as a second argument. By default markdown is false . await ntfy()
.toTopic('home-automation')
.withMessage('You left the front door open')
... await ntfy()
.toTopic('home-automation')
.withMessage('Your garage is **flooding**!', true)
... withTitle(title) - optional Sets the title of the notification. await ntfy()
.toTopic('home-automation')
.withTitle('Garage')
.withMessage('You left the front door open')
... withPriority(priority) - optional Sets the priority of the notification. Possible from lowest to highest priority: min , low , default , high , max await ntfy()
.toTopic('home-automation')
.withMessage('You left the front door open')
.withPriority('high')
... Alternatively you can use dedicated functions: .withMinPriority() , .withLowPriority() , .withDefaultPriority() , .withHighPriority() , .withMaxPriority() await ntfy()
.toTopic('home-automation')
.withMessage('You left the front door open')
.withHighPriority()
... withTags(...tags) - optional Sets tags of the notification. This overrides any previously existing tags. await ntfy()
.toTopic('home-automation')
.withMessage('You left the front door open')
.withTags('door', 'safety')
... withDelay(delay) - optional Sets the delay for notification delivery. Read ntfy docs for more info. await ntfy()
.toTopic('home-automation')
.withMessage('You left the front door open')
.withDelay('tomorrow, 10am')
... withViewAction(label, url, clear?) - optional Adds an action button that opens a website or app when tapped. label - Label of the action button in the notification url - URL to open when action is tapped clear - Clear notification after action button is tapped (defaults to false ) await ntfy()
.toTopic('home-automation')
.withMessage('You left the front door open')
.withViewAction('View Val', 'https://www.val.town/v/xkonti/ntfy')
... withBroadcastAction(label, intent?, extras?, clear?) - optional Adds an action button that sends an Android broadcast intent when tapped. label - Label of the action button in the notification intent - Android intent name, default is io.heckel.ntfy.USER_ACTION extras - Android intent extras. clear - Clear notification after action button is tapped (defaults to false ) await ntfy()
.toTopic('home-automation')
.withMessage('You left the front door open')
.withBroadcastAction('Selfie', 'Take picture', { 'cmd': 'pic' })
... withHtmlAction(label, url, method?, headers?, body?, clear?) - optional Adds an action button that sends a HTTP request when tapped. label - Label of the action button in the notification url - URL to which the HTTP request will be sent method - HTTP method to use for request, default is POST headers - HTTP headers to pass in request. body - HTTP body as a string clear - Clear notification after action button is tapped (defaults to false ) await ntfy()
.toTopic('home-automation')
.withMessage('You left the front door open')
.withHtmlAction(
'Self-destruct',
'https://self.destruct/initiate',
'POST',
{ 'Authentication': 'Bearer 123' },
'{"countdown":60}'
)
... withClickUrl(url) - optional Makes the notification open the specified URL when clicked (tapped). withRawAttachment(filename, filedata) - optional Attached a file to the notification. Only one file can be attached. await ntfy()
.toTopic('home-automation')
.withMessage('You left the front door open')
.withRawAttachment('todo.txt', 'Nothing!')
... withUrlAttachment(url) - optional Attaches a file that is hosted elsewhere (URL). withIcon(url) - optional Sets an icon for the notification. viaEmail(email) - optional Sends the notification via email instead. viaPhoneCall(number) - optional Sends the notification via a phone call . The number defaults to yes , which makes it use the first phone number defined on your ntfy account. withoutCache() - optional Disables the cache for the notification. Read the docs on caching for more info. withoutFirebase() - optional Disables Firebase forwarding for the notification. Read the docs on Firebase for more info. withUnifiedPush() - optional Indicates intent of using the Unified Push for the notification. Read the docs on Unified Push for more info.
beckerBarley
@fil
HTTP
Becker’s Barley trellis SSR chart with Observable Plot This chart is rendered server-side by val.town, using Observable Plot, from data loaded from the GitHub API. For a more complete example, see https://www.val.town/v/fil.earthquakes. For information on this chart, see https://observablehq.com/@observablehq/plot-barley-trellis.
earthquakes
@fil
HTTP
Earthquake map 🌏 This val loads earthquake data from USGS, a topojson file for the land shape, and supporting libraries. It then creates a map and save it as a SVG string. The result is cached for a day. Note that we must strive to keep it under val.town’s limit of 100kB, hence the heavy simplification of the land shape. (For a simpler example, see becker barley .) | | |
|-----|-----|
| Web page | https://fil-earthquakes.web.val.run/ |
| Observable Plot | https://observablehq.com/plot/ |
| linkedom | https://github.com/WebReflection/linkedom |
| topojson | https://github.com/topojson/topojson |
| earthquakes | https://earthquake.usgs.gov |
| world | https://observablehq.com/@visionscarto/world-atlas-topojson |
| css | https://milligram.io/ |
minizod
@zackoverflow
Script
minizod Tiny Zod implementation. Why Zod is a dense library, and its module structure (or lack thereof) makes it difficult for bundlers to tree-shake unused modules . Additionally, using Zod in vals requires the await import syntax which means having to wrap every schema in a Promise and awaiting it. This is extremely annoying. So this is a lil-tiny-smol Zod meant for use in vals. A noteworthy use-case is using minizod to generate tyep-safe API calls to run vals outside of Val Town (such as client-side). Type-safe API call example We can use minizod to create type safe HTTP handlers and generate the corresponding code to call them using Val Town's API, all in a type-safe manner. First, create a schema for a function. The following example defines a schema for a function that takes a { name: string } parameter and returns a Promise<{ text: string }> . const minizodExampleSchema = () =>
@zackoverflow.minizod().chain((z) =>
z
.func()
.args(z.tuple().item(z.object({ name: z.string() })))
.ret(z.promise().return(z.object({ text: z.string() })))
); With a function schema, you can then create an implementation and export it as a val: const minizodExample = @me.minizodExampleSchema().impl(async (
{ name },
) => ({ text: `Hello, ${name}!` })).json() In the above example, we call .impl() on a function schema and pass in a closure which implements the actual body of the function. Here, we simply return a greeting to the name passed in. We can call this val, and it will automatically parse and validate the args we give it: // Errors at compile time and runtime for us!
const response = @me.minizodExample({ name: 420 }) Alternatively, we can use the .json() function to use it as a JSON HTTP handler: const minizodExample = @me.minizodExampleSchema().impl(async (
{ name },
) => ({ text: `Hello, ${name}!` })).json() // <-- this part We can now call minizodExample through Val Town's API. Since we defined a schema for it, we know exactly the types of its arguments and return, which means we can generate type-safe code to call the API: let generatedApiCode =
@zackoverflow.minizodFunctionGenerateTypescript(
// put your username here
"zackoverflow",
"minizodExample",
// put your auth token here
"my auth token",
@me.minizodExampleSchema(),
); This generates the following the code: export const fetchMinizodExample = async (
...args: [{ name: string }]
): Promise<Awaited<Promise<{ text: string }>>> =>
await fetch(`https://api.val.town/v1/run/zackoverflow.minizodExample`, {
method: "POST",
body: JSON.stringify({
args: [...args],
}),
headers: {
Authorization: "Bearer ksafajslfkjal;kjf;laksjl;fajsdf",
},
}).then((res) => res.json());