Announcements¶
The following endpoints are available to manage announcements. Moonraker announcements are effectively push notifications that can be used to notify users of important information related the development and status of software in the Klipper ecosystem. See the appendix for details on how announcements work and recommendations for your implementation.
List announcements¶
Retrieves a list of current announcements.
GET /server/announcements/list?include_dismissed=false
{
"jsonrpc": "2.0",
"method": "server.announcements.list",
"params": {
"include_dismissed": false
},
"id": 4654
}
Parameters
Name | Type | Default | Description |
---|---|---|---|
include_dismissed |
bool | true | When set to false dismissed entries will be excluded from the returned list of current announcements. |
{
"entries": [
{
"entry_id": "arksine/moonlight/issue/3",
"url": "https://github.com/Arksine/moonlight/issues/3",
"title": "Test announcement 3",
"description": "Test Description [with a link](https://moonraker.readthedocs.io).",
"priority": "normal",
"date": 1647459219,
"dismissed": false,
"date_dismissed": null,
"dismiss_wake": null,
"source": "moonlight",
"feed": "moonlight"
},
{
"entry_id": "arksine/moonlight/issue/2",
"url": "https://github.com/Arksine/moonlight/issues/2",
"title": "Announcement Test Two",
"description": "This is a high priority announcement. This line is included in the description.",
"priority": "high",
"date": 1646855579,
"dismissed": false,
"date_dismissed": null,
"dismiss_wake": null,
"source": "moonlight",
"feed": "moonlight"
},
{
"entry_id": "arksine/moonlight/issue/1",
"url": "https://github.com/Arksine/moonlight/issues/1",
"title": "Announcement Test One",
"description": "This is the description. Anything here should appear in the announcement, up to 512 characters.",
"priority": "normal",
"date": 1646854678,
"dismissed": false,
"date_dismissed": null,
"dismiss_wake": null,
"source": "moonlight",
"feed": "moonlight"
},
{
"entry_id": "arksine/moonraker/issue/349",
"url": "https://github.com/Arksine/moonraker/issues/349",
"title": "PolicyKit warnings; unable to manage services, restart system, or update packages",
"description": "This announcement is an effort to get ahead of a coming change that will certainly result in issues. PR #346 has been merged, and with it are some changes to Moonraker's default behavior.",
"priority": "normal",
"date": 1643392406,
"dismissed": false,
"dismiss_wake": null,
"source": "moonlight",
"feed": "Moonraker"
}
],
"feeds": [
"moonraker",
"klipper",
"moonlight"
]
}
Response Specification
Field | Type | Description |
---|---|---|
entries |
[object] | An array of announcement entry objects. The array is sorted by date in descending order (newest to oldest). |
feeds |
[string] | An array of RSS announcement feeds Moonraker is currently subscribed to. |
Field | Type | Description |
---|---|---|
entry_id |
string | A unique identifier for the announcement entry. |
url |
string | A url associated with the announcement. This will link to a GitHub issue for announcements sourced from moonlight . |
title |
string | The title of the announcement. |
description |
string | A brief description of the announcement. For announcement's sourced from moonlight this will be the first paragraph of the associated GitHub issue. Moonlight will truncate truncate descriptions over 512 characters. |
priority |
string | The priority of the announcement. |
date |
int | float | The announcement creation date in unix time. |
dismissed |
bool | Set to true if the announcement has been dismissed. |
date_dismissed |
float | null | The date, in unix time, the announcement was last dismissed. Will be null if the announcement has not been dismissed. |
dismiss_wake |
float | null | The amount of time remaining, in seconds, before the entry's dismissed flag reverts to true . Will be null if the announcement has not been dismissed or if the announcement was dismissed indefinitely. |
source |
string | The source of the announcement. |
feed |
string | The registered RSS feed the announcement belongs to. For announcements sourced internally this will typically be the name of the component that generated the announcement. |
Priority | Description |
---|---|
normal |
Standard priority. Front-end devs should use their own discretion on how to present announcements with normal priority to users. |
high |
High priority. It is recommended that front-ends alert the user when a high priority announcement is received. |
Source | Description |
---|---|
moonlight |
The announcement was received from the moonlight GitHub repo. Announcements received from local XML files when the announcements module is configured in dev_mode will also report the source as moonlight . |
internal |
The announcement was generated by Moonraker itself. This could be a component, such as simplyprint . |
Update announcements¶
Requests that Moonraker check for announcement updates. This is generally not required in production, as Moonraker will automatically check for updates every 30 minutes. However, during development this endpoint is useful to force an update when it is necessary to perform integration tests.
POST /server/announcements/update
{
"jsonrpc": "2.0",
"method": "server.announcements.update",
"id": 4654
}
Returns:
The current list of announcements, in descending order (newest to oldest)
sorted by date
, and a modified
field that contains a boolean value
indicating if the update resulted in a change:
{
"entries": [
{
"entry_id": "arksine/moonraker/issue/349",
"url": "https://github.com/Arksine/moonraker/issues/349",
"title": "PolicyKit warnings; unable to manage services, restart system, or update packages",
"description": "This announcement is an effort to get ahead of a coming change that will certainly result in issues. PR #346 has been merged, and with it are some changes to Moonraker's default behavior.",
"priority": "normal",
"date": 1643392406,
"dismissed": false,
"source": "moonlight",
"feed": "Moonraker"
},
{
"entry_id": "arksine/moonlight/issue/1",
"url": "https://github.com/Arksine/moonlight/issues/1",
"title": "Announcement Test One",
"description": "This is the description. Anything here should appear in the announcement, up to 512 characters.",
"priority": "normal",
"date": 1646854678,
"dismissed": true,
"source": "moonlight",
"feed": "Moonlight"
},
{
"entry_id": "arksine/moonlight/issue/2",
"url": "https://github.com/Arksine/moonlight/issues/2",
"title": "Announcement Test Two",
"description": "This is a high priority announcement. This line is included in the description.",
"priority": "high",
"date": 1646855579,
"dismissed": false,
"source": "moonlight",
"feed": "Moonlight"
},
{
"entry_id": "arksine/moonlight/issue/3",
"url": "https://github.com/Arksine/moonlight/issues/3",
"title": "Test announcement 3",
"description": "Test Description [with a link](https://moonraker.readthedocs.io).",
"priority": "normal",
"date": 1647459219,
"dismissed": false,
"source": "moonlight",
"feed": "Moonlight"
}
],
"modified": false
}
Response Specification
Field | Type | Description |
---|---|---|
entries |
[object] | An array of Announcement Entry objects. |
modified |
bool | A value of true indicates that announcement entries were changed after the update operation. |
Dismiss an announcement¶
Sets the dismiss flag of an announcement to true
.
POST /server/announcements/dismiss
Content-Type: application/json
{
"entry_id": "arksine/moonlight/issue/1",
"wake_time": 600
}
{
"jsonrpc": "2.0",
"method": "server.announcements.dismiss",
"params": {
"entry_id": "arksine/moonlight/issue/1",
"wake_time": 600
},
"id": 4654
}
Parameters
Name | Type | Default | Description |
---|---|---|---|
entry_id |
string | REQUIRED | The entry ID of the announcement to dismiss. |
wake_time |
float | null | null | A time, in seconds, after which the entry's dismiss flag will revert to true . When set to null the flag will remain false indefinitely. |
Tip
The entry_id
typically contains forward slashes. Remember to escape this value
if including it in the query string of an HTTP request.
{
"entry_id": "arksine/moonlight/issue/1"
}
Response Specification
Field | Type | Description |
---|---|---|
entry_id |
string | The entry ID of the dismissed announcement entry. |
List announcement feeds¶
GET /server/announcements/feeds
{
"jsonrpc": "2.0",
"method": "server.announcements.feeds",
"id": 4654
}
{
"feeds": [
"moonraker",
"klipper"
]
}
Response Specification
Field | Type | Description |
---|---|---|
feeds |
[string] | An array of announcement feeds Moonraker is currently subscribed to. |
Subscribe to an announcement feed¶
Subscribes Moonraker to the announcement feed specified in the request.
POST /server/announcements/feed
Content-Type: application/json
{
"name": "my_feed"
}
{
"jsonrpc": "2.0",
"method": "server.announcements.post_feed",
"params": {
"name": "my_feed"
},
"id": 4654
}
Parameters
Name | Type | Default | Description |
---|---|---|---|
name |
string | REQUIRED | The name of the announcement feed to subscribe to. |
{
"feed": "my_feed",
"action": "added"
}
Response Specification
Field | Type | Description |
---|---|---|
feed |
string | The name of the announcement feed subscribed to. |
action |
string | The subscription action taken by Moonraker after the request has been processed. |
Action | Description |
---|---|
added |
The requested announcement feed has been subscribed to. |
skipped |
Moonraker was already subscribed to the requested feed. |
Remove an announcement feed¶
Removes a subscribed feed. Only feeds previously subscribed to using
the subscribe feed endpoint may be
removed. Feeds configured in moonraker.conf
may not be removed.
DELETE /server/announcements/feed?name=my_feed
{
"jsonrpc": "2.0",
"method": "server.announcements.delete_feed",
"params": {
"name": "my_feed"
},
"id": 4654
}
Parameters
Name | Type | Default | Description |
---|---|---|---|
name |
string | REQUIRED | The name of the announcement feed to remove. |
Parameters:
name
: The name of the new feed to remove. This parameter is required.
Returns:
The name of the new feed and the action taken. The action
will be
removed
if the operation was successful.
{
"feed": "my_feed",
"action": "removed"
}
Response Specification
Field | Type | Description |
---|---|---|
feed |
string | The name of the announcement feed removed. |
action |
string | The action taken after the request. Will be removed upon successful removal. |
Tip
Unlike the feed subscription request an
error will be returned if either the feed does not exist or the feed is
configured in moonraker.conf
.
Appendix¶
This section will provide an overview of how the announcement system in Moonraker works, how to set up a dev environment, and provide recommendations on front-end implementation.
How announcements work¶
Moonraker announcements are GitHub issues tagged with the announcement
label. GitHub repos may registered with
moonlight, which is responsible
for generating RSS feeds from GitHub issues using GitHub's REST API. These
RSS feeds are hosted on GitHub Pages, for example Moonraker's feed may be found
here. By
centralizing GitHub API queries in moonlight
we are able to poll multiple
repos without running into API rate limit issues. Moonlight has has a workflow
that checks all registered repos for new announcements every 30 minutes. In
theory it would be able to check for announcements in up to 500 repos before
exceeding GitHub's API rate limit.
Moonraker's [announcements]
component will always check the klipper
and
moonraker
RSS feeds. It is possible to configure additional RSS feeds by
adding them to the subscriptions
option. The component will poll configured
feeds every 30 minutes, resulting in maximum of 1 hour for new announcements
to reach all users.
When new issues are tagged with announcement
these entries will be parsed
and added to the RSS feeds. When the issue is closed they will be removed from
the corresponding feed. Moonlight will fetch up to 20 announcements for each
feed, if a repo goes over this limit older announcements will be removed.
Note
It is also possible for Moonraker to generate announcements itself. For example, if a Moonraker component needs user feedback it may generate an announcement and notify all connected clients. From a front-end's perspective there is no need to treat these announcements differently than any other announcement.
Setting up the dev environment¶
Moonraker provides configuration to parse announcements from a local folder so that it is possible to manually add and remove entries, allowing front-end developers to perform integration tests:
# moonraker.conf
[announcements]
dev_mode: True
With dev_mode
enabled, Moonraker will look formoonraker.xml
and
klipper.xml
in the following folder:
~/moonraker/.devel/announcement_xml
If moonraker is not installed in the home folder then substitute ~
for the parent folder location. This folder is in a hardcoded location
to so as not to expose users to vulnerabilities associated with parsing XML.
It is possible to configure Moonraker to search for your own feeds:
# moonraker.conf
[announcements]
subscription:
my_project
dev_mode: True
The above configuration would look for my_project.xml
in addition to
klipper.xml
and moonraker.xml
. The developer may manually create
the xml feeds or they may clone moonlight
and leverage its script
to generate a feed from issues created on their test repo. When local
feeds have been modified one may call the update announcements API
to have Moonraker fetch the updates and add/remove entries.
RSS file structure¶
Moonlight generates RSS feeds in XML format. Below is an example generated from moonlight's own issue tracker:
<?xml version='1.0' encoding='utf-8'?>
<rss version="2.0" xmlns:moonlight="https://arksine.github.io/moonlight">
<channel>
<title>arksine/moonlight</title>
<link>https://github.com/Arksine/moonlight</link>
<description>RSS Announcements for Moonraker</description>
<pubDate>Tue, 22 Mar 2022 23:19:04 GMT</pubDate>
<moonlight:configHash>f2912192bf0d09cf18d8b8af22b2d3501627043e5afa3ebff0e45e4794937901</moonlight:configHash>
<item>
<title>Test announcement 3</title>
<link>https://github.com/Arksine/moonlight/issues/3</link>
<description>Test Description [with a link](https://moonraker.readthedocs.io).</description>
<pubDate>Wed, 16 Mar 2022 19:33:39 GMT</pubDate>
<category>normal</category>
<guid>arksine/moonlight/issue/3</guid>
</item>
<item>
<title>Announcement Test Two</title>
<link>https://github.com/Arksine/moonlight/issues/2</link>
<description>This is a high priority announcement. This line is included in the description.</description>
<pubDate>Wed, 09 Mar 2022 19:52:59 GMT</pubDate>
<category>high</category>
<guid>arksine/moonlight/issue/2</guid>
</item>
<item>
<title>Announcement Test One</title>
<link>https://github.com/Arksine/moonlight/issues/1</link>
<description>This is the description. Anything here should appear in the announcement, up to 512 characters.</description>
<pubDate>Wed, 09 Mar 2022 19:37:58 GMT</pubDate>
<category>normal</category>
<guid>arksine/moonlight/issue/1</guid>
</item>
</channel>
</rss>
Each xml file may contain only one <rss>
element, and each <rss>
element
may contain only one channel. All items must be present aside from
moonlight:configHash
, which is used by the workflow to detect changes to
moonlight's configuration. Most elements are self explanatory, developers will
be most interested in adding and removing <item>
elements, as these are
the basis for entries in Moonraker's announcement database.
Generating announcements from your own repo¶
As mentioned previously, its possible to clone moonlight and use its rss script to generate announcements from issues in your repo:
cd ~
git clone https://github.com/arksine/moonlight
cd moonlight
virtualenv -p /usr/bin/python3 .venv
source .venv/bin/activate
pip install httpx[http2]
deactivate
To add your repo edit ~/moonlight/src/config.json
:
{
"moonraker": {
"repo_owner": "Arksine",
"repo_name": "moonraker",
"description": "API Host For Klipper",
"authorized_creators": ["Arksine"]
},
"klipper": {
"repo_owner": "Klipper3d",
"repo_name": "klipper",
"description": "A 3D Printer Firmware",
"authorized_creators": ["KevinOConnor"]
},
// Add your test repo info here. It should contain
// fields matching those in "moonraker" and "klipper"
// shown above.
}
Once your repo is added, create one or more issues on your GitHub
repo tagged with the announcement
label. Add the critical
label to
one if you wish to test high priority announcements. You may need to
create these labels in your repo before they can be added.
Now we can use moonlight to generate the xml files:
cd ~/moonlight
source .venv/bin/activate
src/update_rss.py
deactivate
After the script has run it will generate the configured RSS feeds
and store them in ~/moonlight/assets
. If using this method it may
be useful to create a symbolic link to it in Moonraker's devel folder:
cd ~/moonraker
mkdir .devel
cd .devel
ln -s ~/moonlight/assets announcement_xml
If you haven't done so, configure Moonraker to subscribe to your feed and restart the Moonraker service. Otherwise you may call the announcement update API to have Moonraker parse the announcements from your test feed.
Implementation details and recommendations¶
When a front-end first connects to Moonraker it is recommended that the list announcements API is called to retrieve the current list of announcement entries. If the front-end is connected via websocket it may watch for the announcement update and announcement dismissed JSON-RPC notifications and update its UI accordingly.
Front-end devs should decide how they want to present announcements to users.
They could be treated as any other notification, for example a front-end
may have a notification icon that shows the current number of unread
announcements. Front-ends can mark an announcement as read
by calling
the dismiss announcement endpoint. Any
announcement entry with dismissed == true
should be considered read.
When a high priority
announcement is detected it is recommended that
clients present the announcement in a format that is immediately visible
to the user. That said, it may be wise to allow users to opt out of
this behavior via configuration.
Note
If an announcement is dismissed, closed on GitHub, then reopened,
the dismissed
flag will reset to false. This is expected behavior
as announcements are pruned from the database when they are no
longer present in feeds. It isn't valid for repo maintainers
to re-open a closed announcement. That said, its fine to close
and re-open issues during development and testing using repos
that are not yet registered with moonlight.