Readme

Allows to publish a ntfy notification using a fluent builder configuration.

Usage example

Create valimport { 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.

Create valimport { 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.

Create valawait ntfy() .asUser('user123', '12345') ...

usingToken(token) - optional

Authenticates using the provided token. Please use ValTown's secrets for this.

Create valawait ntfy() .usingToken('some-token') ...

toTopic(topic) - required

Specifies which topic to publish the message to.

Create valawait 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.

Create valawait ntfy() .toTopic('home-automation') .withMessage('You left the front door open') ...
Create valawait ntfy() .toTopic('home-automation') .withMessage('Your garage is **flooding**!', true) ...

withTitle(title) - optional

Sets the title of the notification.

Create valawait 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

Create valawait ntfy() .toTopic('home-automation') .withMessage('You left the front door open') .withPriority('high') ...

Alternatively you can use dedicated functions: .withMinPriority(), .withLowPriority(), .withDefaultPriority(), .withHighPriority(), .withMaxPriority()

Create valawait 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.

Create valawait 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.

Create valawait 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)
Create valawait 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)
Create valawait 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)
Create valawait 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.

Create valawait 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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
export const ntfy = () => {
class NtfyPublisher {
private serverUrl: string = "ntfy.sh";
private topic: string | null = null;
private message: string | null = null;
private file: any | null = null;
private actions: string[] = [];
private headers: Record<string, string> = {};
toServer(url: string): NtfyPublisher {
this.serverUrl = url;
return this;
}
asUser(user: string, password: string) {
const payload = btoa(`${user}:${password}`);
this.headers["Authorization"] = `Basic ${payload}`;
return this;
}
usingToken(token: string) {
this.headers["Authorization"] = `Bearer ${token}`;
return this;
}
toTopic(topic: string) {
this.topic = topic;
return this;
}
withMessage(message: string, markdown = false) {
if (this.file != null)
throw new Error(
"A file is already attached. Can't send a message and a file at once.",
);
if (this.message != null)
throw new Error(
"A message has been already set. Can't send 2 messages at once.",
);
this.message = message;
if (markdown) {
this.headers["Markdown"] = "yes";
}
return this;
}
withTitle(title: string) {
this.headers["Title"] = title;
return this;
}
withPriority(priority: "min" | "low" | "default" | "high" | "max") {
this.headers["Priority"] = priority;
return this;
}
withMinPriority() {
return this.withPriority("min");
}
withLowPriority() {
return this.withPriority("low");
}
withDefaultPriority() {
return this.withPriority("default");
}
withHighPriority() {
return this.withPriority("high");
}
withMaxPriority() {
return this.withPriority("max");
}
withTags(...tags: string[]) {
this.headers["Tags"] = tags.join(",");
return this;
}
withDelay(when: string) {
this.headers["Delay"] = when;
return this;
}
withViewAction(label: string, url: string, clear: string | null = null) {
if (this.actions.length >= 3) {
throw new Error("Max of 3 actions are allowed.");
}
this.actions.push(
`view, ${label}, ${url}${clear == null ? "" : ", clear=" + clear}`,
);
return this;
}
withBroadcastAction(
label: string,
intent: string | null = null,
extras: Record<string, string> = {},
clear: boolean | null = null,
) {
if (this.actions.length >= 3) {
throw new Error("Max of 3 actions are allowed.");
}
const segments = ["broadcast", label];
if (intent != null) {
segments.push(`intent=${intent}`);
}
if (clear != null) {
segments.push(`clear=${clear}`);
}
for (const [key, value] of Object.entries(extras)) {
segments.push(`extras.${key}=${value}`);
}
this.actions.push(segments.join(", "));
👆 This is a val. Vals are TypeScript snippets of code, written in the browser and run on our servers. Create scheduled functions, email yourself, and persist small pieces of data — all from the browser.