More docs
This commit is contained in:
		
							parent
							
								
									c5ec3b48b4
								
							
						
					
					
						commit
						a779434bab
					
				
					 7 changed files with 1776 additions and 1045 deletions
				
			
		
							
								
								
									
										838
									
								
								docs/publish.md
									
										
									
									
									
								
							
							
						
						
									
										838
									
								
								docs/publish.md
									
										
									
									
									
								
							|  | @ -821,11 +821,13 @@ Here's an example of what that a notification with actions can look like: | |||
|   <figcaption>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</figcaption> | ||||
| </figure> | ||||
| 
 | ||||
| To define the user actions, you can either pass the `actions` field as part of the JSON body (if you're  | ||||
| [publishing via JSON](#publish-as-json)), or use the `X-Actions` header (or any of its aliases: `Actions`, `Action`). | ||||
| You can set up to three user actions in your notifications, using either of the following methods: | ||||
| 
 | ||||
| Using the `X-Actions` header and the **simple format** (details see below), you can create the above notification like  | ||||
| this. This format is much **easier to write, but less powerful**: | ||||
| * In the `X-Actions` header, using the **simple format** | ||||
| * As a **JSON array** in the `actions` key, when [publishing as JSON](#publish-as-json)  | ||||
| 
 | ||||
| Using the `X-Actions` header (or any of its aliases: `Actions`, `Action`) and the **simple format** (details see below), you  | ||||
| can create the above notification like this.  | ||||
| 
 | ||||
| === "Command line (curl)" | ||||
|     ``` | ||||
|  | @ -899,40 +901,29 @@ this. This format is much **easier to write, but less powerful**: | |||
|         ] | ||||
|     ])); | ||||
|     ``` | ||||
| 
 | ||||
| The `X-Actions` header (including above-mentioned aliases) supports the following formats: | ||||
|   | ||||
| Here's the generic definition of the simple format:  | ||||
| 
 | ||||
| === "Simple format (long)" | ||||
|     ``` | ||||
|     X-Actions: action=<action>, label=<label>, param1=..., param2=..., ... | ||||
|     ``` | ||||
|     Simple format examples: | ||||
|     ``` | ||||
|     X-Actions: action=view, label=Play video, url=https://www.youtube.com/watch?v=EmL3lS0-Sr8 | ||||
|     X-Actions: action=broadcast, label=Turn of flashlight, extras.cmd=flashlight-on | ||||
|     X-Actions: action=http, label=Change temperature, url=https://api.nest.com/device/XZ1D2, body=target_temp_f=65 | ||||
|     action=<action1>, label=<label1>, paramN=...[; action=<action2>, label=<label2>, ...] | ||||
|     ``` | ||||
| 
 | ||||
| === "Simple format (short)" | ||||
|     ``` | ||||
|     Actions: <action>, <label>, param1=..., param2=..., ... | ||||
|     <action1>, <label1>, paramN=...[; <action2>, <label2>, ...] | ||||
|     ``` | ||||
| 
 | ||||
| An `action` is either [`view`](#open-websiteapp), [`broadcast`](#send-android-broadcast), or [`http`](#send-http-request), | ||||
| and the `label` defines the button text. The other parameters depend on the action itself. Please refer to this table | ||||
| for all available parameters: | ||||
| `action=` and `label=` are optional in all actions, and `url=` is optional in the `view` and `http` action. | ||||
| 
 | ||||
| | Field     | Required | Type                           | Example               | Applies to action | Description                                                  | | ||||
| |-----------|----------|--------------------------------|-----------------------|-------------------|--------------------------------------------------------------| | ||||
| | `action`  | ✔️       | *view, broadcast, or http*     | `view`                | *all actions*     | Action type                                                  | | ||||
| | `label`   | ✔️       | *string*                       | `Turn on light`       | *all actions*     | Label of the action button in the notification               | | ||||
| | `url`     | -️       | *URL*                          | `https://example.com` | `view`, `http`    | URL to open or send a HTTP request to                        | | ||||
| | `method`  | -️       | *HTTP method (GET, POST, ...)* | `GET`                 | `http`            | HTTP method to use for HTTP request (**default is `POST`**!) | | ||||
| | `headers` | -️       | *HTTP method (GET, POST, ...)* | `GET`                 | `http`            | HTTP method to use for HTTP request (**default is `POST`**!) | | ||||
| | `method`  | -️       | *HTTP method (GET, POST, ...)* | `GET`                 | `http`            | HTTP method to use for HTTP request (**default is `POST`**!) | | ||||
| Simple format examples: | ||||
| 
 | ||||
| ``` | ||||
| http, Change temp, https://api.nest.com/XZ1D2, body=target_temp=65 | ||||
| action=view, label=Open site, url=https://ntfy.sh; action=broadcast, label=Turn off, extras.cmd=turn-off | ||||
| ``` | ||||
| 
 | ||||
| Alternatively, you can define actions as **JSON array** (details see below), and pass them as part of the JSON body  | ||||
| Alternatively, the same actions can be defined as **JSON array** (details see below), if the notification is defined as part of the JSON body  | ||||
| (see [publish as JSON](#publish-as-json)): | ||||
| 
 | ||||
| === "Command line (curl)" | ||||
|  | @ -1127,15 +1118,241 @@ Alternatively, you can define actions as **JSON array** (details see below), and | |||
|     ])); | ||||
|     ``` | ||||
| 
 | ||||
| XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx | ||||
| 
 | ||||
| 
 | ||||
| ### Open website/app | ||||
| The `view` action opens a website or app when the action button is tapped, e.g. a browser, a Google Maps location, or | ||||
| even a deep link into Twitter or a show ntfy topic. | ||||
| 
 | ||||
| XXXXXXXXXXXXXXXXXXx | ||||
| Examples: | ||||
| 
 | ||||
| * `http://` or `https://` will open your browser (or an app if it registered for a URL) | ||||
| * `mailto:` links will open your mail app, e.g. `mailto:phil@example.com` | ||||
| * `geo:` links will open Google Maps, e.g. `geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+CA` | ||||
| * `ntfy://` links will open ntfy (see [ntfy:// links](subscribe/phone.md#ntfy-links)), e.g. `ntfy://ntfy.sh/stats` | ||||
| * ... | ||||
| 
 | ||||
| Here's an example using the simple format: | ||||
| 
 | ||||
| === "Command line (curl)" | ||||
|     ``` | ||||
|     curl \ | ||||
|         -d "You left the house. Turn down the A/C?" \ | ||||
|         -H "Actions: view, Open portal, https://home.nest.com/" \ | ||||
|     ntfy.sh/myhome | ||||
|     ``` | ||||
| 
 | ||||
| === "ntfy CLI" | ||||
|     ``` | ||||
|     ntfy publish \ | ||||
|         --actions="view, Open portal, https://home.nest.com/" \ | ||||
|         myhome \ | ||||
|         "You left the house. Turn down the A/C?" | ||||
|     ``` | ||||
| 
 | ||||
| === "HTTP" | ||||
|     ``` http | ||||
|     POST /myhome HTTP/1.1 | ||||
|     Host: ntfy.sh | ||||
|     Actions: view, Open portal, https://home.nest.com/ | ||||
| 
 | ||||
|     You left the house. Turn down the A/C? | ||||
|     ``` | ||||
| 
 | ||||
| === "JavaScript" | ||||
|     ``` javascript | ||||
|     fetch('https://ntfy.sh/myhome', { | ||||
|         method: 'POST', | ||||
|         body: 'You left the house. Turn down the A/C?', | ||||
|         headers: {  | ||||
|             'Actions': 'view, Open portal, https://home.nest.com/'  | ||||
|         } | ||||
|     }) | ||||
|     ``` | ||||
| 
 | ||||
| === "Go" | ||||
|     ``` go | ||||
|     req, _ := http.NewRequest("POST", "https://ntfy.sh/myhome", strings.NewReader("You left the house. Turn down the A/C?")) | ||||
|     req.Header.Set("Actions", "view, Open portal, https://home.nest.com/") | ||||
|     http.DefaultClient.Do(req) | ||||
|     ``` | ||||
| 
 | ||||
| === "PowerShell" | ||||
|     ``` powershell | ||||
|     $uri = "https://ntfy.sh/myhome" | ||||
|     $headers = @{ Actions="view, Open portal, https://home.nest.com/" } | ||||
|     $body = "You left the house. Turn down the A/C?" | ||||
|     Invoke-RestMethod -Method 'Post' -Uri $uri -Headers $headers -Body $body -UseBasicParsing | ||||
|     ``` | ||||
| 
 | ||||
| === "Python" | ||||
|     ``` python | ||||
|     requests.post("https://ntfy.sh/myhome", | ||||
|         data="You left the house. Turn down the A/C?", | ||||
|         headers={ "Actions": "view, Open portal, https://home.nest.com/" }) | ||||
|     ``` | ||||
| 
 | ||||
| === "PHP" | ||||
|     ``` php-inline | ||||
|     file_get_contents('https://ntfy.sh/reddit_alerts', false, stream_context_create([ | ||||
|         'http' => [ | ||||
|             'method' => 'POST', | ||||
|             'header' => | ||||
|                 "Content-Type: text/plain\r\n" . | ||||
|                 "Actions: view, Open portal, https://home.nest.com/", | ||||
|             'content' => 'You left the house. Turn down the A/C?' | ||||
|         ] | ||||
|     ])); | ||||
|     ``` | ||||
| 
 | ||||
| And the same example using [JSON publishing](#publish-as-json): | ||||
| 
 | ||||
| === "Command line (curl)" | ||||
|     ``` | ||||
|     curl ntfy.sh \ | ||||
|       -d '{ | ||||
|         "topic": "myhome", | ||||
|         "message": "You left the house. Turn down the A/C?", | ||||
|         "actions": [ | ||||
|           { | ||||
|             "action": "view", | ||||
|             "label": "Open portal", | ||||
|             "url": "https://home.nest.com/" | ||||
|           } | ||||
|         ] | ||||
|       }' | ||||
|     ``` | ||||
| 
 | ||||
| === "ntfy CLI" | ||||
|     ``` | ||||
|     ntfy publish \ | ||||
|         --actions '[ | ||||
|             { | ||||
|                 "action": "view", | ||||
|                 "label": "Open portal", | ||||
|                 "url": "https://home.nest.com/" | ||||
|             } | ||||
|         ]' \ | ||||
|         myhome \ | ||||
|         "You left the house. Turn down the A/C?" | ||||
|     ``` | ||||
| 
 | ||||
| === "HTTP" | ||||
|     ``` http | ||||
|     POST / HTTP/1.1 | ||||
|     Host: ntfy.sh | ||||
| 
 | ||||
|     { | ||||
|         "topic": "myhome", | ||||
|         "message": "You left the house. Turn down the A/C?", | ||||
|         "actions": [ | ||||
|           { | ||||
|             "action": "view", | ||||
|             "label": "Open portal", | ||||
|             "url": "https://home.nest.com/" | ||||
|           } | ||||
|         ] | ||||
|     } | ||||
|     ``` | ||||
| 
 | ||||
| === "JavaScript" | ||||
|     ``` javascript | ||||
|     fetch('https://ntfy.sh', { | ||||
|         method: 'POST', | ||||
|         body: JSON.stringify({ | ||||
|             topic: "myhome", | ||||
|             message": "You left the house. Turn down the A/C?", | ||||
|             actions: [ | ||||
|                 { | ||||
|                     action: "view", | ||||
|                     label: "Open portal", | ||||
|                     url: "https://home.nest.com/" | ||||
|                 } | ||||
|             ] | ||||
|         }) | ||||
|     }) | ||||
|     ``` | ||||
| 
 | ||||
| === "Go" | ||||
|     ``` go | ||||
|     // You should probably use json.Marshal() instead and make a proper struct, | ||||
|     // but for the sake of the example, this is easier. | ||||
|      | ||||
|     body := `{ | ||||
|         "topic": "myhome", | ||||
|         "message": "You left the house. Turn down the A/C?", | ||||
|         "actions": [ | ||||
|           { | ||||
|             "action": "view", | ||||
|             "label": "Open portal", | ||||
|             "url": "https://home.nest.com/" | ||||
|           } | ||||
|         ] | ||||
|     }` | ||||
|     req, _ := http.NewRequest("POST", "https://ntfy.sh/", strings.NewReader(body)) | ||||
|     http.DefaultClient.Do(req) | ||||
|     ``` | ||||
| 
 | ||||
| === "PowerShell" | ||||
|     ``` powershell | ||||
|     $uri = "https://ntfy.sh" | ||||
|     $body = @{ | ||||
|         "topic"="myhome" | ||||
|         "message"="You left the house. Turn down the A/C?" | ||||
|         "actions"=@( | ||||
|             @{ | ||||
|                 "action"="view" | ||||
|                 "label"="Open portal" | ||||
|                 "url"="https://home.nest.com/" | ||||
|             } | ||||
|         ) | ||||
|     } | ConvertTo-Json | ||||
|     Invoke-RestMethod -Method 'Post' -Uri $uri -Body $body -ContentType "application/json" -UseBasicParsing | ||||
|     ``` | ||||
| 
 | ||||
| === "Python" | ||||
|     ``` python | ||||
|     requests.post("https://ntfy.sh/", | ||||
|         data=json.dumps({ | ||||
|             "topic": "myhome", | ||||
|             "message": "You left the house. Turn down the A/C?", | ||||
|             "actions": [ | ||||
|                 { | ||||
|                     "action": "view", | ||||
|                     "label": "Open portal", | ||||
|                     "url": "https://home.nest.com/" | ||||
|                 } | ||||
|             ] | ||||
|         }) | ||||
|     ) | ||||
|     ``` | ||||
| 
 | ||||
| === "PHP" | ||||
|     ``` php-inline | ||||
|     file_get_contents('https://ntfy.sh/', false, stream_context_create([ | ||||
|         'http' => [ | ||||
|             'method' => 'POST', | ||||
|             'header' => "Content-Type: application/json", | ||||
|             'content' => json_encode([ | ||||
|                 "topic": "myhome", | ||||
|                 "message": "You left the house. Turn down the A/C?", | ||||
|                 "actions": [ | ||||
|                     [ | ||||
|                         "action": "view", | ||||
|                         "label": "Open portal", | ||||
|                         "url": "https://home.nest.com/" | ||||
|                     ] | ||||
|                 ] | ||||
|             ]) | ||||
|         ] | ||||
|     ])); | ||||
|     ``` | ||||
| 
 | ||||
| The `view` action supports the following fields: | ||||
| 
 | ||||
| | Field    | Required | Type     | Example               | Description                                    | | ||||
| |----------|----------|----------|-----------------------|------------------------------------------------| | ||||
| | `action` | ✔️       | *string* | `view`                | Action type (**must be `view`**)               | | ||||
| | `label`  | ✔️       | *string* | `Turn on light`       | Label of the action button in the notification | | ||||
| | `url`    | ✔️       | *URL*    | `https://example.com` | URL to open when action is tapped              | | ||||
| 
 | ||||
| ### Send Android broadcast | ||||
| The `broadcast` action sends an [Android broadcast](https://developer.android.com/guide/components/broadcasts) intent | ||||
|  | @ -1144,107 +1361,510 @@ or [Tasker](https://play.google.com/store/apps/details?id=net.dinglisch.android. | |||
| you can do everything your phone is capable of. Examples include taking pictures, launching/killing apps, change device | ||||
| settings, write/read files, etc. | ||||
| 
 | ||||
| XXXXXXXXXXXXXXxx | ||||
| Here's an example using the simple format: | ||||
| 
 | ||||
| === "Command line (curl)" | ||||
|     ``` | ||||
|     curl \ | ||||
|         -d "Your wife requested you send a picture of yourself." \ | ||||
|         -H "Actions: broadcast, Take picture, extras.cmd=pic, extras.camera=front" \ | ||||
|     ntfy.sh/wifey | ||||
|     ``` | ||||
| 
 | ||||
| === "ntfy CLI" | ||||
|     ``` | ||||
|     ntfy publish \ | ||||
|         --actions="broadcast, Take picture, extras.cmd=pic, extras.camera=front" \ | ||||
|         wifey \ | ||||
|         "Your wife requested you send a picture of yourself." | ||||
|     ``` | ||||
| 
 | ||||
| === "HTTP" | ||||
|     ``` http | ||||
|     POST /wifey HTTP/1.1 | ||||
|     Host: ntfy.sh | ||||
|     Actions: broadcast, Take picture, extras.cmd=pic, extras.camera=front | ||||
| 
 | ||||
|     Your wife requested you send a picture of yourself. | ||||
|     ``` | ||||
| 
 | ||||
| === "JavaScript" | ||||
|     ``` javascript | ||||
|     fetch('https://ntfy.sh/wifey', { | ||||
|         method: 'POST', | ||||
|         body: 'Your wife requested you send a picture of yourself.', | ||||
|         headers: {  | ||||
|             'Actions': 'broadcast, Take picture, extras.cmd=pic, extras.camera=front'  | ||||
|         } | ||||
|     }) | ||||
|     ``` | ||||
| 
 | ||||
| === "Go" | ||||
|     ``` go | ||||
|     req, _ := http.NewRequest("POST", "https://ntfy.sh/wifey", strings.NewReader("Your wife requested you send a picture of yourself.")) | ||||
|     req.Header.Set("Actions", "broadcast, Take picture, extras.cmd=pic, extras.camera=front") | ||||
|     http.DefaultClient.Do(req) | ||||
|     ``` | ||||
| 
 | ||||
| === "PowerShell" | ||||
|     ``` powershell | ||||
|     $uri = "https://ntfy.sh/wifey" | ||||
|     $headers = @{ Actions="broadcast, Take picture, extras.cmd=pic, extras.camera=front" } | ||||
|     $body = "Your wife requested you send a picture of yourself." | ||||
|     Invoke-RestMethod -Method 'Post' -Uri $uri -Headers $headers -Body $body -UseBasicParsing | ||||
|     ``` | ||||
| 
 | ||||
| === "Python" | ||||
|     ``` python | ||||
|     requests.post("https://ntfy.sh/wifey", | ||||
|         data="Your wife requested you send a picture of yourself.", | ||||
|         headers={ "Actions": "broadcast, Take picture, extras.cmd=pic, extras.camera=front" }) | ||||
|     ``` | ||||
| 
 | ||||
| === "PHP" | ||||
|     ``` php-inline | ||||
|     file_get_contents('https://ntfy.sh/wifey', false, stream_context_create([ | ||||
|         'http' => [ | ||||
|             'method' => 'POST', | ||||
|             'header' => | ||||
|                 "Content-Type: text/plain\r\n" . | ||||
|                 "Actions: broadcast, Take picture, extras.cmd=pic, extras.camera=front", | ||||
|             'content' => 'Your wife requested you send a picture of yourself.' | ||||
|         ] | ||||
|     ])); | ||||
|     ``` | ||||
| 
 | ||||
| And the same example using [JSON publishing](#publish-as-json): | ||||
| 
 | ||||
| === "Command line (curl)" | ||||
|     ``` | ||||
|     curl ntfy.sh \ | ||||
|       -d '{ | ||||
|         "topic": "wifey", | ||||
|         "message": "Your wife requested you send a picture of yourself.", | ||||
|         "actions": [ | ||||
|           { | ||||
|             "action": "broadcast", | ||||
|             "label": "Take picture", | ||||
|             "extras": { | ||||
|                 "cmd": "pic", | ||||
|                 "camera": "front" | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|       }' | ||||
|     ``` | ||||
| 
 | ||||
| === "ntfy CLI" | ||||
|     ``` | ||||
|     ntfy publish \ | ||||
|         --actions '[ | ||||
|             { | ||||
|                 "action": "broadcast", | ||||
|                 "label": "Take picture", | ||||
|                 "extras": { | ||||
|                     "cmd": "pic", | ||||
|                     "camera": "front" | ||||
|                 } | ||||
|             } | ||||
|         ]' \ | ||||
|         wifey \ | ||||
|         "Your wife requested you send a picture of yourself." | ||||
|     ``` | ||||
| 
 | ||||
| === "HTTP" | ||||
|     ``` http | ||||
|     POST / HTTP/1.1 | ||||
|     Host: ntfy.sh | ||||
| 
 | ||||
|     { | ||||
|         "topic": "wifey", | ||||
|         "message": "Your wife requested you send a picture of yourself.", | ||||
|         "actions": [ | ||||
|           { | ||||
|             "action": "broadcast", | ||||
|             "label": "Take picture", | ||||
|             "extras": { | ||||
|                 "cmd": "pic", | ||||
|                 "camera": "front" | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|     } | ||||
|     ``` | ||||
| 
 | ||||
| === "JavaScript" | ||||
|     ``` javascript | ||||
|     fetch('https://ntfy.sh', { | ||||
|         method: 'POST', | ||||
|         body: JSON.stringify({ | ||||
|             topic: "wifey", | ||||
|             message": "Your wife requested you send a picture of yourself.", | ||||
|             actions: [ | ||||
|                 { | ||||
|                     "action": "broadcast", | ||||
|                     "label": "Take picture", | ||||
|                     "extras": { | ||||
|                         "cmd": "pic", | ||||
|                         "camera": "front" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }) | ||||
|     }) | ||||
|     ``` | ||||
| 
 | ||||
| === "Go" | ||||
|     ``` go | ||||
|     // You should probably use json.Marshal() instead and make a proper struct, | ||||
|     // but for the sake of the example, this is easier. | ||||
|      | ||||
|     body := `{ | ||||
|         "topic": "wifey", | ||||
|         "message": "Your wife requested you send a picture of yourself.", | ||||
|         "actions": [ | ||||
|           { | ||||
|             "action": "broadcast", | ||||
|             "label": "Take picture", | ||||
|             "extras": { | ||||
|                 "cmd": "pic", | ||||
|                 "camera": "front" | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|     }` | ||||
|     req, _ := http.NewRequest("POST", "https://ntfy.sh/", strings.NewReader(body)) | ||||
|     http.DefaultClient.Do(req) | ||||
|     ``` | ||||
| 
 | ||||
| === "PowerShell" | ||||
|     ``` powershell | ||||
|     $uri = "https://ntfy.sh" | ||||
|     $body = @{ | ||||
|         "topic"="wifey" | ||||
|         "message"="Your wife requested you send a picture of yourself." | ||||
|         "actions"=@( | ||||
|             @{ | ||||
|                 "action"="broadcast" | ||||
|                 "label"="Take picture" | ||||
|                 "extras"=@{ | ||||
|                     "cmd"="pic" | ||||
|                     "camera"="front" | ||||
|                 } | ||||
|             } | ||||
|         ) | ||||
|     } | ConvertTo-Json | ||||
|     Invoke-RestMethod -Method 'Post' -Uri $uri -Body $body -ContentType "application/json" -UseBasicParsing | ||||
|     ``` | ||||
| 
 | ||||
| === "Python" | ||||
|     ``` python | ||||
|     requests.post("https://ntfy.sh/", | ||||
|         data=json.dumps({ | ||||
|             "topic": "wifey", | ||||
|             "message": "Your wife requested you send a picture of yourself.", | ||||
|             "actions": [ | ||||
|                 { | ||||
|                     "action": "broadcast", | ||||
|                     "label": "Take picture", | ||||
|                     "extras": { | ||||
|                         "cmd": "pic", | ||||
|                         "camera": "front" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }) | ||||
|     ) | ||||
|     ``` | ||||
| 
 | ||||
| === "PHP" | ||||
|     ``` php-inline | ||||
|     file_get_contents('https://ntfy.sh/', false, stream_context_create([ | ||||
|         'http' => [ | ||||
|             'method' => 'POST', | ||||
|             'header' => "Content-Type: application/json", | ||||
|             'content' => json_encode([ | ||||
|                 "topic": "wifey", | ||||
|                 "message": "Your wife requested you send a picture of yourself.", | ||||
|                 "actions": [ | ||||
|                     [ | ||||
|                     "action": "broadcast", | ||||
|                     "label": "Take picture", | ||||
|                     "extras": [ | ||||
|                         "cmd": "pic", | ||||
|                         "camera": "front" | ||||
|                     ] | ||||
|                 ] | ||||
|             ]) | ||||
|         ] | ||||
|     ])); | ||||
|     ``` | ||||
| 
 | ||||
| The `broadcast` action supports the following fields: | ||||
| 
 | ||||
| | Field    | Required | Type             | Example                 | Description                                                                                                                                                                            | | ||||
| |----------|----------|------------------|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ||||
| | `action` | ✔️       | *string*         | `broadcast`             | Action type (**must be `broadcast`**)                                                                                                                                                  | | ||||
| | `label`  | ✔️       | *string*         | `Turn on light`         | Label of the action button in the notification                                                                                                                                         | | ||||
| | `intent` | -️       | *string*         | `com.example.AN_INTENT` | Android intent name, **default is `io.heckel.ntfy.USER_ACTION`**                                                                                                                       | | ||||
| | `extras` | -️       | *map of strings* | *see above*             | Android intent extras. Currently, only string extras are supported. When publishing as JSON, extras are passed as a map. When the simple format is used, use `extras.<param>=<value>`. | | ||||
| 
 | ||||
| ### Send HTTP request | ||||
| The `http` action sends a HTTP POST/GET/PUT request when the action button is tapped. You can use this to trigger REST APIs | ||||
| for whatever systems you have, e.g. opening the garage door, or turning on/off lights. | ||||
| 
 | ||||
| XXXXXXXXXXXXXXXXXXXXx | ||||
| Here's an example using the simple format: | ||||
| 
 | ||||
| === "`view` action" | ||||
|     ``` json | ||||
|     {  | ||||
|       "action": "view",  | ||||
|       "label": "Open bing.com",  | ||||
|       "url": "https://bing.com" | ||||
|     } | ||||
| === "Command line (curl)" | ||||
|     ``` | ||||
|     curl \ | ||||
|         -d "Garage door has been open for 15 minutes. Close it?" \ | ||||
|         -H "Actions: http, Cloor door, https://mygarage.lan/close, headers.Authorization=Bearer zAzsx1sk.." \ | ||||
|         ntfy.sh/myhome | ||||
|     ``` | ||||
| 
 | ||||
| === "`broadcast` action" | ||||
|     ``` json | ||||
|     {  | ||||
|       "action": "broadcast",  | ||||
|       "label": "Send broadcast",  | ||||
|       "intent": "io.heckel.ntfy.USER_ACTION", | ||||
|       "extras": { | ||||
|         "param": "this is a param", | ||||
|         "anotherparam": "this is another one" | ||||
|       } | ||||
|     } | ||||
| === "ntfy CLI" | ||||
|     ``` | ||||
|     ntfy publish \ | ||||
|         --actions="http, Cloor door, https://mygarage.lan/close, headers.Authorization=Bearer zAzsx1sk.." \ | ||||
|         myhome \ | ||||
|         "Garage door has been open for 15 minutes. Close it?" | ||||
|     ``` | ||||
| 
 | ||||
| === "`http` action" | ||||
|     ``` json | ||||
|     {  | ||||
|       "action": "http",  | ||||
|       "label": "Take picture",  | ||||
|       "method": "POST", | ||||
|       "url": "https://homecam.lan/capture", | ||||
|       "headers": { | ||||
|         "Authorization": "..." | ||||
|       }, | ||||
|       "body": "this is a message" | ||||
|     } | ||||
| === "HTTP" | ||||
|     ``` http | ||||
|     POST /myhome HTTP/1.1 | ||||
|     Host: ntfy.sh | ||||
|     Actions: http, Cloor door, https://mygarage.lan/close, headers.Authorization=Bearer zAzsx1sk.. | ||||
| 
 | ||||
|     Garage door has been open for 15 minutes. Close it? | ||||
|     ``` | ||||
| 
 | ||||
| Examples: | ||||
| 
 | ||||
| === "Open a website" | ||||
|     ``` json | ||||
|     {  | ||||
|       "action": "view",  | ||||
|       "label": "Open bing.com",  | ||||
|       "url": "https://bing.com" | ||||
|     } | ||||
| === "JavaScript" | ||||
|     ``` javascript | ||||
|     fetch('https://ntfy.sh/myhome', { | ||||
|         method: 'POST', | ||||
|         body: 'Garage door has been open for 15 minutes. Close it?', | ||||
|         headers: {  | ||||
|             'Actions': 'http, Cloor door, https://mygarage.lan/close, headers.Authorization=Bearer zAzsx1sk..'  | ||||
|         } | ||||
|     }) | ||||
|     ``` | ||||
| 
 | ||||
| === "Open location in Google Maps" | ||||
|     ``` json | ||||
|     {  | ||||
|       "action": "view",  | ||||
|       "label": "Show map",  | ||||
|       "url": "geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California" | ||||
|     } | ||||
| === "Go" | ||||
|     ``` go | ||||
|     req, _ := http.NewRequest("POST", "https://ntfy.sh/myhome", strings.NewReader("Garage door has been open for 15 minutes. Close it?")) | ||||
|     req.Header.Set("Actions", "http, Cloor door, https://mygarage.lan/close, headers.Authorization=Bearer zAzsx1sk..") | ||||
|     http.DefaultClient.Do(req) | ||||
|     ``` | ||||
| 
 | ||||
| === "Open a ntfy topic (deep link)" | ||||
|     ``` json | ||||
| === "PowerShell" | ||||
|     ``` powershell | ||||
|     $uri = "https://ntfy.sh/myhome" | ||||
|     $headers = @{ Actions="http, Cloor door, https://mygarage.lan/close, headers.Authorization=Bearer zAzsx1sk.." } | ||||
|     $body = "Garage door has been open for 15 minutes. Close it?" | ||||
|     Invoke-RestMethod -Method 'Post' -Uri $uri -Headers $headers -Body $body -UseBasicParsing | ||||
|     ``` | ||||
| 
 | ||||
| === "Python" | ||||
|     ``` python | ||||
|     requests.post("https://ntfy.sh/myhome", | ||||
|         data="Garage door has been open for 15 minutes. Close it?", | ||||
|         headers={ "Actions": "http, Cloor door, https://mygarage.lan/close, headers.Authorization=Bearer zAzsx1sk.." }) | ||||
|     ``` | ||||
| 
 | ||||
| === "PHP" | ||||
|     ``` php-inline | ||||
|     file_get_contents('https://ntfy.sh/reddit_alerts', false, stream_context_create([ | ||||
|         'http' => [ | ||||
|             'method' => 'POST', | ||||
|             'header' => | ||||
|                 "Content-Type: text/plain\r\n" . | ||||
|                 "Actions: http, Cloor door, https://mygarage.lan/close, headers.Authorization=Bearer zAzsx1sk..", | ||||
|             'content' => 'Garage door has been open for 15 minutes. Close it?' | ||||
|         ] | ||||
|     ])); | ||||
|     ``` | ||||
| 
 | ||||
| And the same example using [JSON publishing](#publish-as-json): | ||||
| 
 | ||||
| === "Command line (curl)" | ||||
|     ``` | ||||
|     curl ntfy.sh \ | ||||
|       -d '{ | ||||
|         "topic": "myhome", | ||||
|         "message": "Garage door has been open for 15 minutes. Close it?", | ||||
|         "actions": [ | ||||
|           { | ||||
|             "action": "http", | ||||
|             "label": "Close door", | ||||
|             "url": "https://mygarage.lan/close", | ||||
|             "headers": { | ||||
|                 "Authorization": "Bearer zAzsx1sk.." | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|       }' | ||||
|     ``` | ||||
| 
 | ||||
| === "ntfy CLI" | ||||
|     ``` | ||||
|     ntfy publish \ | ||||
|         --actions '[ | ||||
|             { | ||||
|               "action": "http", | ||||
|               "label": "Close door", | ||||
|               "url": "https://mygarage.lan/close", | ||||
|               "headers": { | ||||
|                 "Authorization": "Bearer zAzsx1sk.." | ||||
|               } | ||||
|             } | ||||
|         ]' \ | ||||
|         myhome \ | ||||
|         "Garage door has been open for 15 minutes. Close it?" | ||||
|     ``` | ||||
| 
 | ||||
| === "HTTP" | ||||
|     ``` http | ||||
|     POST / HTTP/1.1 | ||||
|     Host: ntfy.sh | ||||
| 
 | ||||
|     { | ||||
|       "action": "view", | ||||
|       "label": "Show stats", | ||||
|       "url": "ntfy://ntfy.sh/stats" | ||||
|         "topic": "myhome", | ||||
|         "message": "Garage door has been open for 15 minutes. Close it?", | ||||
|         "actions": [ | ||||
|           { | ||||
|             "action": "http", | ||||
|             "label": "Close door", | ||||
|             "url": "https://mygarage.lan/close", | ||||
|             "headers": { | ||||
|               "Authorization": "Bearer zAzsx1sk.." | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|     } | ||||
|     ``` | ||||
| 
 | ||||
| === "Send broadcast" | ||||
|     ``` json | ||||
|     { | ||||
|       "action": "broadcast", | ||||
|       "label": "Send broadcast", | ||||
|       "intent": "my.custom.intent", | ||||
|       "extras": { | ||||
|         "message": "whats up, hello" | ||||
|       } | ||||
|     }   | ||||
| === "JavaScript" | ||||
|     ``` javascript | ||||
|     fetch('https://ntfy.sh', { | ||||
|         method: 'POST', | ||||
|         body: JSON.stringify({ | ||||
|             topic: "myhome", | ||||
|             message": "Garage door has been open for 15 minutes. Close it?", | ||||
|             actions: [ | ||||
|               { | ||||
|                 "action": "http", | ||||
|                 "label": "Close door", | ||||
|                 "url": "https://mygarage.lan/close", | ||||
|                 "headers": { | ||||
|                   "Authorization": "Bearer zAzsx1sk.." | ||||
|                 } | ||||
|               } | ||||
|             ] | ||||
|         }) | ||||
|     }) | ||||
|     ``` | ||||
| 
 | ||||
| === "Send a ntfy message" | ||||
|     ``` json | ||||
|     {  | ||||
|       "action": "http",  | ||||
|       "label": "Send message",  | ||||
|       "method": "POST", | ||||
|       "url": "http://ntfy.example.com/mytopic", | ||||
|       "headers": { | ||||
|         "Title": "another message", | ||||
|         "Tags": "tag1, tag2" | ||||
|       }, | ||||
|       "body": "this is a message" | ||||
|     } | ||||
| === "Go" | ||||
|     ``` go | ||||
|     // You should probably use json.Marshal() instead and make a proper struct, | ||||
|     // but for the sake of the example, this is easier. | ||||
|      | ||||
|     body := `{ | ||||
|         "topic": "myhome", | ||||
|         "message": "Garage door has been open for 15 minutes. Close it?", | ||||
|         "actions": [ | ||||
|           { | ||||
|             "action": "http", | ||||
|             "label": "Close door", | ||||
|             "url": "https://mygarage.lan/close", | ||||
|             "headers": { | ||||
|               "Authorization": "Bearer zAzsx1sk.." | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|     }` | ||||
|     req, _ := http.NewRequest("POST", "https://ntfy.sh/", strings.NewReader(body)) | ||||
|     http.DefaultClient.Do(req) | ||||
|     ``` | ||||
| 
 | ||||
| === "PowerShell" | ||||
|     ``` powershell | ||||
|     $uri = "https://ntfy.sh" | ||||
|     $body = @{ | ||||
|         "topic"="myhome" | ||||
|         "message"="Garage door has been open for 15 minutes. Close it?" | ||||
|         "actions"=@( | ||||
|             @{ | ||||
|                 "action"="http", | ||||
|                 "label"="Close door" | ||||
|                 "url"="https://mygarage.lan/close" | ||||
|                 "headers"=@{ | ||||
|                   "Authorization"="Bearer zAzsx1sk.." | ||||
|                 } | ||||
|             } | ||||
|           } | ||||
|         ) | ||||
|     } | ConvertTo-Json | ||||
|     Invoke-RestMethod -Method 'Post' -Uri $uri -Body $body -ContentType "application/json" -UseBasicParsing | ||||
|     ``` | ||||
| 
 | ||||
| === "Python" | ||||
|     ``` python | ||||
|     requests.post("https://ntfy.sh/", | ||||
|         data=json.dumps({ | ||||
|             "topic": "myhome", | ||||
|             "message": "Garage door has been open for 15 minutes. Close it?", | ||||
|             "actions": [ | ||||
|                 { | ||||
|                   "action": "http", | ||||
|                   "label": "Close door", | ||||
|                   "url": "https://mygarage.lan/close", | ||||
|                   "headers": { | ||||
|                     "Authorization": "Bearer zAzsx1sk.." | ||||
|                   } | ||||
|                 } | ||||
|             ] | ||||
|         }) | ||||
|     ) | ||||
|     ``` | ||||
| 
 | ||||
| === "PHP" | ||||
|     ``` php-inline | ||||
|     file_get_contents('https://ntfy.sh/', false, stream_context_create([ | ||||
|         'http' => [ | ||||
|             'method' => 'POST', | ||||
|             'header' => "Content-Type: application/json", | ||||
|             'content' => json_encode([ | ||||
|                 "topic": "myhome", | ||||
|                 "message": "Garage door has been open for 15 minutes. Close it?", | ||||
|                 "actions": [ | ||||
|                     [ | ||||
|                         "action": "http", | ||||
|                         "label": "Close door", | ||||
|                         "url": "https://mygarage.lan/close", | ||||
|                         "headers": [ | ||||
|                             "Authorization": "Bearer zAzsx1sk.." | ||||
|                          ] | ||||
|                     ] | ||||
|                 ] | ||||
|             ]) | ||||
|         ] | ||||
|     ])); | ||||
|     ``` | ||||
| 
 | ||||
| The `http` action supports the following fields: | ||||
| 
 | ||||
| | Field     | Required | Type               | Example                   | Description                                                                                                                                             | | ||||
| |-----------|----------|--------------------|---------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------| | ||||
| | `action`  | ✔️       | *string*           | `http`                    | Action type (**must be `http`**)                                                                                                                        | | ||||
| | `label`   | ✔️       | *string*           | `Open garage door`        | Label of the action button in the notification                                                                                                          | | ||||
| | `url`     | ✔️       | *string*           | `https://ntfy.sh/mytopic` | URL to which the HTTP request will be sent                                                                                                              | | ||||
| | `method`  | -️       | *GET/POST/PUT/...* | `GET`                     | HTTP method to use for request, **default is POST (!)**                                                                                                 | | ||||
| | `headers` | -️       | *map of strings*   | *see above*               | HTTP headers to pass in request. When publishing as JSON, headers are passed as a map. When the simple format is used, use `headers.<header1>=<value>`. | | ||||
| | `method`  | -️       | *string*           | `some body, somebody?`    | HTTP body                                                                                                                                               | | ||||
| 
 | ||||
| ## Click action | ||||
| You can define which URL to open when a notification is clicked. This may be useful if your notification is related  | ||||
| to a Zabbix alert or a transaction that you'd like to provide the deep-link for. Tapping the notification will open | ||||
|  | @ -1257,9 +1877,9 @@ by another app, the responsible app may open. | |||
| Examples: | ||||
| 
 | ||||
| * `http://` or `https://` will open your browser (or an app if it registered for a URL) | ||||
| * `mailto:` links will open your mail app | ||||
| * `geo:` links will open Google Maps (or your maps application) | ||||
| * `ntfy://` links will open ntfy (see [ntfy:// links](subscribe/phone.md#ntfy-links)) | ||||
| * `mailto:` links will open your mail app, e.g. `mailto:phil@example.com` | ||||
| * `geo:` links will open Google Maps, e.g. `geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+CA` | ||||
| * `ntfy://` links will open ntfy (see [ntfy:// links](subscribe/phone.md#ntfy-links)), e.g. `ntfy://ntfy.sh/stats` | ||||
| * ... | ||||
| 
 | ||||
| Here's an example that will open Reddit when the notification is clicked: | ||||
|  |  | |||
							
								
								
									
										27
									
								
								go.mod
									
										
									
									
									
								
							
							
						
						
									
										27
									
								
								go.mod
									
										
									
									
									
								
							|  | @ -4,7 +4,7 @@ go 1.17 | |||
| 
 | ||||
| require ( | ||||
| 	cloud.google.com/go/firestore v1.6.1 // indirect | ||||
| 	cloud.google.com/go/storage v1.21.0 // indirect | ||||
| 	cloud.google.com/go/storage v1.22.0 // indirect | ||||
| 	firebase.google.com/go v3.13.0+incompatible | ||||
| 	github.com/BurntSushi/toml v1.1.0 // indirect | ||||
| 	github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect | ||||
|  | @ -15,20 +15,20 @@ require ( | |||
| 	github.com/olebedev/when v0.0.0-20211212231525-59bd4edcf9d6 | ||||
| 	github.com/stretchr/testify v1.7.0 | ||||
| 	github.com/urfave/cli/v2 v2.4.0 | ||||
| 	golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 | ||||
| 	golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect | ||||
| 	golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 | ||||
| 	golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect | ||||
| 	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c | ||||
| 	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 | ||||
| 	golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 | ||||
| 	google.golang.org/api v0.74.0 | ||||
| 	golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 | ||||
| 	golang.org/x/time v0.0.0-20220411224347-583f2d630306 | ||||
| 	google.golang.org/api v0.75.0 | ||||
| 	gopkg.in/yaml.v2 v2.4.0 | ||||
| ) | ||||
| 
 | ||||
| require github.com/pkg/errors v0.9.1 | ||||
| 
 | ||||
| require ( | ||||
| 	cloud.google.com/go v0.100.2 // indirect | ||||
| 	cloud.google.com/go/compute v1.5.0 // indirect | ||||
| 	cloud.google.com/go v0.101.0 // indirect | ||||
| 	cloud.google.com/go/compute v1.6.0 // indirect | ||||
| 	cloud.google.com/go/iam v0.3.0 // indirect | ||||
| 	github.com/AlekSi/pointer v1.2.0 // indirect | ||||
| 	github.com/davecgh/go-spew v1.1.1 // indirect | ||||
|  | @ -36,16 +36,17 @@ require ( | |||
| 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect | ||||
| 	github.com/golang/protobuf v1.5.2 // indirect | ||||
| 	github.com/google/go-cmp v0.5.7 // indirect | ||||
| 	github.com/googleapis/gax-go/v2 v2.2.0 // indirect | ||||
| 	github.com/googleapis/gax-go/v2 v2.3.0 // indirect | ||||
| 	github.com/googleapis/go-type-adapters v1.0.0 // indirect | ||||
| 	github.com/pmezard/go-difflib v1.0.0 // indirect | ||||
| 	github.com/russross/blackfriday/v2 v2.1.0 // indirect | ||||
| 	go.opencensus.io v0.23.0 // indirect | ||||
| 	golang.org/x/net v0.0.0-20220403103023-749bd193bc2b // indirect | ||||
| 	golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12 // indirect | ||||
| 	golang.org/x/net v0.0.0-20220420153159-1850ba15e1be // indirect | ||||
| 	golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect | ||||
| 	golang.org/x/text v0.3.7 // indirect | ||||
| 	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect | ||||
| 	golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect | ||||
| 	google.golang.org/appengine v1.6.7 // indirect | ||||
| 	google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf // indirect | ||||
| 	google.golang.org/genproto v0.0.0-20220420195807-44278fea765b // indirect | ||||
| 	google.golang.org/grpc v1.45.0 // indirect | ||||
| 	google.golang.org/protobuf v1.28.0 // indirect | ||||
| 	gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect | ||||
|  |  | |||
							
								
								
									
										35
									
								
								go.sum
									
										
									
									
									
								
							
							
						
						
									
										35
									
								
								go.sum
									
										
									
									
									
								
							|  | @ -29,6 +29,8 @@ cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2Z | |||
| cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= | ||||
| cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y= | ||||
| cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= | ||||
| cloud.google.com/go v0.101.0 h1:g+LL+JvpvdyGtcaD2xw2mSByE/6F9s471eJSoaysM84= | ||||
| cloud.google.com/go v0.101.0/go.mod h1:hEiddgDb77jDQ+I80tURYNJEnuwPzFU8awCFFRLKjW0= | ||||
| cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= | ||||
| cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= | ||||
| cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= | ||||
|  | @ -40,6 +42,8 @@ cloud.google.com/go/compute v1.2.0/go.mod h1:xlogom/6gr8RJGBe7nT2eGsQYAFUbbv8dbC | |||
| cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= | ||||
| cloud.google.com/go/compute v1.5.0 h1:b1zWmYuuHz7gO9kDcM/EpHGr06UgsYNRpNJzI2kFiLM= | ||||
| cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= | ||||
| cloud.google.com/go/compute v1.6.0 h1:XdQIN5mdPTSBVwSIVDuY5e8ZzVAccsHvD3qTEz4zIps= | ||||
| cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= | ||||
| cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= | ||||
| cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= | ||||
| cloud.google.com/go/firestore v1.6.1 h1:8rBq3zRjnHx8UtBvaOWqBB1xq9jH6/wltfQLlTMh2Fw= | ||||
|  | @ -58,6 +62,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX | |||
| cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= | ||||
| cloud.google.com/go/storage v1.21.0 h1:HwnT2u2D309SFDHQII6m18HlrCi3jAXhUMTLOWXYH14= | ||||
| cloud.google.com/go/storage v1.21.0/go.mod h1:XmRlxkgPjlBONznT2dDUU/5XlpU2OjMnKuqnZI01LAA= | ||||
| cloud.google.com/go/storage v1.22.0 h1:NUV0NNp9nkBuW66BFRLuMgldN60C57ET3dhbwLIYio8= | ||||
| cloud.google.com/go/storage v1.22.0/go.mod h1:GbaLEoMqbVm6sx3Z0R++gSiBlgMv6yUi2q1DeGFKQgE= | ||||
| dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | ||||
| firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4= | ||||
| firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs= | ||||
|  | @ -166,6 +172,8 @@ github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIG | |||
| github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= | ||||
| github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= | ||||
| github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= | ||||
| github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= | ||||
| github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= | ||||
| github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | ||||
| github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | ||||
| github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | ||||
|  | @ -188,6 +196,10 @@ github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pf | |||
| github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= | ||||
| github.com/googleapis/gax-go/v2 v2.2.0 h1:s7jOdKSaksJVOxE0Y/S32otcfiP+UQ0cL8/GTKaONwE= | ||||
| github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= | ||||
| github.com/googleapis/gax-go/v2 v2.3.0 h1:nRJtk3y8Fm770D42QV6T90ZnvFZyk7agSo3Q+Z9p3WI= | ||||
| github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= | ||||
| github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA= | ||||
| github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= | ||||
| github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= | ||||
| github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||
| github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= | ||||
|  | @ -248,6 +260,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U | |||
| golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o= | ||||
| golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | ||||
| golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= | ||||
| golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | ||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | ||||
|  | @ -325,6 +339,9 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su | |||
| golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= | ||||
| golang.org/x/net v0.0.0-20220403103023-749bd193bc2b h1:vI32FkLJNAWtGD4BwkThwEy6XS7ZLLMHkSkYfF8M0W0= | ||||
| golang.org/x/net v0.0.0-20220403103023-749bd193bc2b/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= | ||||
| golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= | ||||
| golang.org/x/net v0.0.0-20220420153159-1850ba15e1be h1:yx80W7nvY5ySWpaU8UWaj5o9e23YgO9BRhQol7Lc+JI= | ||||
| golang.org/x/net v0.0.0-20220420153159-1850ba15e1be/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= | ||||
| golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||
| golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
|  | @ -345,6 +362,8 @@ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ | |||
| golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= | ||||
| golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a h1:qfl7ob3DIEs3Ml9oLuPwY2N04gymzAW04WsUQHIClgM= | ||||
| golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= | ||||
| golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE= | ||||
| golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= | ||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
|  | @ -414,9 +433,13 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc | |||
| golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12 h1:QyVthZKMsyaQwBTJE04jdNN0Pp5Fn9Qga0mrgxyERQM= | ||||
| golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= | ||||
| golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
| golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= | ||||
| golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||||
| golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8= | ||||
| golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||||
| golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
|  | @ -432,6 +455,8 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb | |||
| golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs= | ||||
| golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= | ||||
| golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= | ||||
|  | @ -488,6 +513,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T | |||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= | ||||
| golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= | ||||
| golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= | ||||
| google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= | ||||
| google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= | ||||
|  | @ -528,6 +555,8 @@ google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/S | |||
| google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= | ||||
| google.golang.org/api v0.74.0 h1:ExR2D+5TYIrMphWgs5JCgwRhEDlPDXXrLwHHMgPHTXE= | ||||
| google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= | ||||
| google.golang.org/api v0.75.0 h1:0AYh/ae6l9TDUvIQrDw5QRpM100P6oHgD+o3dYHMzJg= | ||||
| google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= | ||||
| google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | ||||
| google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
| google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
|  | @ -575,6 +604,7 @@ google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6D | |||
| google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= | ||||
| google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= | ||||
| google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= | ||||
| google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= | ||||
|  | @ -613,6 +643,11 @@ google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2 | |||
| google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= | ||||
| google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf h1:JTjwKJX9erVpsw17w+OIPP7iAgEkN/r8urhWSunEDTs= | ||||
| google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= | ||||
| google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= | ||||
| google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= | ||||
| google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= | ||||
| google.golang.org/genproto v0.0.0-20220420195807-44278fea765b h1:5zvsLqz9A1TKTeI6AhjJH/Vkaw0GGBs+D3GkvUUqNO0= | ||||
| google.golang.org/genproto v0.0.0-20220420195807-44278fea765b/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= | ||||
| google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | ||||
| google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= | ||||
| google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= | ||||
|  |  | |||
|  | @ -90,7 +90,7 @@ const ( | |||
| 
 | ||||
| // Schema management queries | ||||
| const ( | ||||
| 	currentSchemaVersion          = 5 | ||||
| 	currentSchemaVersion          = 6 | ||||
| 	createSchemaVersionTableQuery = ` | ||||
| 		CREATE TABLE IF NOT EXISTS schemaVersion ( | ||||
| 			id INT PRIMARY KEY, | ||||
|  | @ -168,6 +168,11 @@ const ( | |||
| 		ALTER TABLE messages_new RENAME TO messages; | ||||
| 		COMMIT; | ||||
| 	` | ||||
| 
 | ||||
| 	// 5 -> 6 | ||||
| 	migrate5To6AlterMessagesTableQuery = ` | ||||
| 		ALTER TABLE messages ADD COLUMN actions TEXT NOT NULL DEFAULT(''); | ||||
| 	` | ||||
| ) | ||||
| 
 | ||||
| type messageCache struct { | ||||
|  | @ -509,6 +514,8 @@ func setupCacheDB(db *sql.DB) error { | |||
| 		return migrateFrom3(db) | ||||
| 	} else if schemaVersion == 4 { | ||||
| 		return migrateFrom4(db) | ||||
| 	} else if schemaVersion == 5 { | ||||
| 		return migrateFrom5(db) | ||||
| 	} | ||||
| 	return fmt.Errorf("unexpected schema version found: %d", schemaVersion) | ||||
| } | ||||
|  | @ -581,5 +588,16 @@ func migrateFrom4(db *sql.DB) error { | |||
| 	if _, err := db.Exec(updateSchemaVersion, 5); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return migrateFrom5(db) | ||||
| } | ||||
| 
 | ||||
| func migrateFrom5(db *sql.DB) error { | ||||
| 	log.Print("Migrating cache database schema: from 5 to 6") | ||||
| 	if _, err := db.Exec(migrate5To6AlterMessagesTableQuery); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, err := db.Exec(updateSchemaVersion, 6); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil // Update this when a new version is added | ||||
| } | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ package server | |||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"heckel.io/ntfy/util" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
|  | @ -96,7 +95,10 @@ func parseActionsFromSimple(s string) ([]*action, error) { | |||
| 	actions := make([]*action, 0) | ||||
| 	rawActions := util.SplitNoEmpty(s, ";") | ||||
| 	for _, rawAction := range rawActions { | ||||
| 		newAction := &action{} | ||||
| 		newAction := &action{ | ||||
| 			Headers: make(map[string]string), | ||||
| 			Extras:  make(map[string]string), | ||||
| 		} | ||||
| 		parts := util.SplitNoEmpty(rawAction, ",") | ||||
| 		if len(parts) < 3 { | ||||
| 			return nil, fmt.Errorf("cannot parse action: action requires at least keys 'action', 'label' and one parameter: %s", rawAction) | ||||
|  | @ -109,6 +111,10 @@ func parseActionsFromSimple(s string) ([]*action, error) { | |||
| 				newAction.Label = value | ||||
| 			} else if key == "" && util.InStringList([]string{"view", "http"}, newAction.Action) && i == 2 { | ||||
| 				newAction.URL = value | ||||
| 			} else if strings.HasPrefix(key, "headers.") { | ||||
| 				newAction.Headers[strings.TrimPrefix(key, "headers.")] = value | ||||
| 			} else if strings.HasPrefix(key, "extras.") { | ||||
| 				newAction.Extras[strings.TrimPrefix(key, "extras.")] = value | ||||
| 			} else if key != "" { | ||||
| 				switch strings.ToLower(key) { | ||||
| 				case "action": | ||||
|  | @ -122,10 +128,10 @@ func parseActionsFromSimple(s string) ([]*action, error) { | |||
| 				case "body": | ||||
| 					newAction.Body = value | ||||
| 				default: | ||||
| 					return nil, errors.Errorf("cannot parse action: key '%s' not supported, please use JSON format instead", part) | ||||
| 					return nil, fmt.Errorf("cannot parse action: key '%s' not supported, please use JSON format instead", part) | ||||
| 				} | ||||
| 			} else { | ||||
| 				return nil, errors.Errorf("cannot parse action: unknown phrase '%s'", part) | ||||
| 				return nil, fmt.Errorf("cannot parse action: unknown phrase '%s'", part) | ||||
| 			} | ||||
| 		} | ||||
| 		actions = append(actions, newAction) | ||||
|  |  | |||
|  | @ -61,4 +61,22 @@ func TestParseActions(t *testing.T) { | |||
| 	require.Equal(t, "https://door.lan/open", actions[0].URL) | ||||
| 	require.Equal(t, "PUT", actions[0].Method) | ||||
| 	require.Equal(t, "this is a body", actions[0].Body) | ||||
| 
 | ||||
| 	actions, err = parseActions("action=broadcast, label=Do a thing, extras.command=some command, extras.some_param=a parameter") | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, 1, len(actions)) | ||||
| 	require.Equal(t, "broadcast", actions[0].Action) | ||||
| 	require.Equal(t, "Do a thing", actions[0].Label) | ||||
| 	require.Equal(t, 2, len(actions[0].Extras)) | ||||
| 	require.Equal(t, "some command", actions[0].Extras["command"]) | ||||
| 	require.Equal(t, "a parameter", actions[0].Extras["some_param"]) | ||||
| 
 | ||||
| 	actions, err = parseActions("action=http, label=Send request, url=http://example.com, method=GET, headers.Content-Type=application/json, headers.Authorization=Basic sdasffsf") | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, 1, len(actions)) | ||||
| 	require.Equal(t, "http", actions[0].Action) | ||||
| 	require.Equal(t, "Send request", actions[0].Label) | ||||
| 	require.Equal(t, 2, len(actions[0].Headers)) | ||||
| 	require.Equal(t, "application/json", actions[0].Headers["Content-Type"]) | ||||
| 	require.Equal(t, "Basic sdasffsf", actions[0].Headers["Authorization"]) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										1869
									
								
								web/package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										1869
									
								
								web/package-lock.json
									
										
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue