- English
- 中文版
A programmable website notifications aggregator
Original project:BlackGlory/Gloria
Timed a custom JavaScript code task, implement the automation work such as web monitoring or forum check-in, and pop up notifications.
Table of contents
- Go to the Chrome Web Store to download and install.
- Start the browser and type
chrome://extensions/
in the address bar to enter Extensions Page, click the switch in the upper right corner of the webpage to turn on "Developer mode":
- a. Go to the Releases to download the extension
crx
file. Drag to Extensions Page to install. - b. Go to the Releases to download the extension package
zip
file and unzip it. And click "Load unpacked" button, select the root directory of the previously unzipped file to complete the installation of the extension.
- Go to the Microsoft Edge Addons to download and install.
- Go to the Releases to download
xpi
file and install. (Because the permissionunsafe-eval
is declared, it can not be put on Mozilla Add-ons.)
JavaScript is the only programming language supported by Gloria-X. The task creates the notification by passing a Gloria Notification
object or an array composed of this object to a specific callback function commit
. Gloria-X internally processes the parameter of the commit
function and determines the content of the push notification based on the parameter.
For example, the following simple task code is actually runnable, which executes after each set interval and pops up a notification about the timestamp:
commit({
message: Date.now().toString(),
});
Of course, this task code itself is meaningless, just a simple example.
The display content of the notification message depends on the passed attribute value of the Gloria Notification
object, so first you need to understand the structure of the object.
The structure of the Gloria Notification
object as follows:
{
title: String, // The default is "", the title of the push notification
message: String, // The default is "", the content of the push notification
iconUrl: String, // The default is undefined, the icon of the push notification
imageUrl: String, // The default is undefined, an image displayed on the push notification (Firefox notification ignores this attribute)
url: String, // The default is undefined, the URL opened when the push notification is clicked
id: String, // The default is undefined, which is used to detemine whether the notification is the same. If there is no speciffic requirement, there is generally no need to specify manually
}
For security and to avoid the possibility of some unknown errors, the extension will ignore other attributes on the Gloria Notification
object during internal processing.
Tip: All object attribute types above are optional
String
. When writing task code, do not directly pass other types that may be implicitly converted toString
(such as:Number
,Boolean
, etc.). If you do so, the extension will think that the passed attribute type is error, so be sure to manually convert the value to theString
type by youself.
title
- Type:
String
- Default:
""
- Meaning: The title of the push notification.
- This attribute value will be used to detemine with the old notification in the STAGES cache.
- For example: in an Observation task, if the
title
value of the new object is different from thetitle
value of the old object in the STAGES cache, the new object will be jedged as a new notification and need to be pushed.
- Type:
message
- Type:
String
- Default:
""
- Meaning: The body content or description of the notification
- This attribute value will be used to detemine with the old notification in the STAGES cache.
- Tip: If you need to treat two objects with the same
title
andmessage
attribute values as two different notifications for a requirement, you can specify wto differentid
attribute values for the object, these two objects will then be treated as two different objects.
- Type:
iconUrl
- Type:
String
- Default:
undefined
- Meaning: The icon information displayed in the push notification.
- If the
iconUrl
attribute is no specified in the object, the extension provides a default icon value of"icons/app/icon-128.png"
(the Gloria-X icon).
- Type:
imageUrl
- Type:
String
- Default:
undefined
- Meaning: The image displayed on the push notification.
- Firefox notificaions ignore this attribute, meaning that the image is not displayed in notification, but is displayed in the notification history.
- Type:
url
- Type:
String
- Default:
undefined
- Meaning: The URL that opens when the push notification is clicked.
- Type:
id
- Type:
String
- Default:
undefined
- Meaning: Object id, which can be used to detemine with the old notification in the STAGES cache.
- Type:
The commit
function is a special function proviede by Gloria-X to the task code. The task code is written by passing the result of the data to be observed in the task code, the Gloria Notification
object or an array of that object, as a parameter to commit
.
According to the difference of the parameters passed to commit
, tasks are divided into "Observation task" and "General task".
When a single Gloria Notification
object is passed to the commit
function, the task will be recognized as an "Observation task", which compares the Gloria Notification
object obtained each time with the Gloria Notification
object recorded last time, and pushes a new notification when it is not the same.
Note: If null
or undefined
is returned in the Observation task, the execution result is ignored, not cached and used for comparison.
When an array of Gloria Notification
objects is passed to the commit
function, the task will be recognized as a "General task", and each collection is cached in an internal "Stages" component so that the notification is pushed only when the object is new.
The observing task will respond to changes in each result, based on the previous execution result; and the general task will only respond to new results, and do not handle past repeated execution results.
The difference between two types of tasks is explained in detail in two working task codes.
Suppose there are two tasks, one for the observation task and one for the general task.
//* Observation task, a single object is passed in
(async () => {
const { lodash: _ } = await importScripts('gloria-utils');
return {
title: _.random(1).toString(),
};
})().then(commit);
//* General task, an array is passed in
(async () => {
const { lodash: _ } = await importScripts('gloria-utils');
return [
{
title: _.random(1).toString(),
},
];
})().then(commit);
Of course, these two tasks have no practical meaning, only for testing and referencing the difference between the two tasks.
We used the random
method in the lodash
library for testing. Don't worry about what importScripts ('gloria-utils')
is, as we will mention later, we only need to focus on the results of two tasks delivered to the commit
function. In both tasks, the title
attribute of the object is used _.random(1).toString()
assignment, the result of which has only two possibilities, "0"
or "1"
.
When you actually run these two tasks, you will find:
In the Observation task, it is possible for the watch task to push new notification each time tha task is executed, as long as the title
value of the object this time is different form the title
value of the previous object.
The behavior of the General task is completely different. When you do it enough times, you will find that the General task only push a new notification once at most.
Because when the task is created, the task will be executed once, and the result of the first execution will be cached in the STAGES component. When a different result appears in the subsequent execution, a new notification will be pushed and the new result will be cached in the STAGES component. It is important that the General task is different from the Observation task, is not remove old results, but will be cached in the STAEGS component along with the new results. And because of the particularity of this example task, there are only two possible results, no matter what result the subsequent execution will get, it will be the same as one of the wto results cached in the STAGES component, which is impossible to push new notifications.
These are examples of the differencess between the two tasks, which can be observed in the internal state view panel of the options page.
In a task code, the commit
function is required. a Gloria Notification
object, Gloria Notification
array, null
or undefined
prompt should be sent to the commit
function at the end of any code.
For example, in the following example task code:
(async () => {
const { lodash: _ } = await importScripts('gloria-utils');
const result = _.random(1);
if (result) {
commit({
title: result.toString(),
});
}
})();
When the result
in the above code is 0
, since no value was provided to the commit
function, the task will need to wait for a timeout(60s
) error before ending execution. So the above task code should be normalized to:
(async () => {
const { lodash: _ } = await importScripts('gloria-utils');
const result = _.random(1);
if (result) {
commit({
title: result.toString(),
});
} else {
commit(null);
}
})();
Now that you know how to create notifications in the task code, the next step is to access the URL.
There are two access url in task code:
fetch
You can check the usage of fetch
in the Fetch API. Note that fetch
in the Gloria-X environment automatically appends the destination URL Cookie when creating the request, so that you can take advantage of the login status on the destination website. Of course, the displayed content of such a push notification may contain your private information. The speciffic display content depends on how the Gloria Notification
object is written.
XMLHttpRequest(XHR)
You can check the usage of XHR
in XMLHttpRequest. Unlike fetch
, XHR
in Gloria-X does not automatically append Cookie to the target URL and can not get login status on the target website.
If you plan to use XHR
, you may need to use a similar Promise
encapsulation method in your task code:
// Encapsulation method
function ajax(url, method = 'GET', headers = []) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url);
headers.forEach(head => {
xhr.setRequestHeader(head.name, head.value);
});
xhr.responseType = 'json';
xhr.onload = res => {
resolve(res.target.response);
};
xhr.onerror = res => {
reject(res.target.response);
};
xhr.send();
});
}
// Use ajax
ajax('...').then( ... ).catch( ... )
For example, create a simple Observation task that detects the latest version of this project:
fetch('https://api.github.com/repos/LightAPIs/Gloria-X/releases/latest')
.then(res => res.json())
.then(json => {
const message = json.tag_name;
const url = json.html_url;
commit({
title: 'Gloria-X',
message,
url,
});
});
I also found a runnable task on Gloria's task code sharing website.
fetch('https://api-savecoins.nznweb.com.br/v1/games?currency=USD&locale=en&order=discount_begin_date_desc&filter[on_sale]=true')
.then(res => res.json())
.then(json => {
let notifications = json.data
.map(feed => {
return {
title: '[' + feed.platform.slice(0, 1).toUpperCase() + '] Before ' + feed.price_info.discountPrice.discountEndsAt.split('T')[0],
message:
'[' +
feed.price_info.country.code +
'] ' +
feed.price_info.discountPrice.originalDiscountPrice +
' (-' +
feed.price_info.discountPrice.percentOff +
'%)\n' +
feed.title,
url: 'https://savecoins.app/game/' + feed.slug,
iconUrl: feed.imageUrl,
disc: feed.price_info.discountPrice.percentOff,
};
})
.filter(({ disc: x }) => x > 80);
commit(notifications);
});
If you know fetch
or XHR
, you should be able to understand how to access the URL in the task code through these tasks.
While the URL is already accessible in the task code, not all sites have an API similar to the JSON format data returned in the example above. At this time, you need access the URL directly and read the HTML content, and then process the HTML through modules such as cheerio
.
In the Gloria-X, external scripts can be loaded via the importScripts
method like a Web Worker.
It should be noted that, unlike the Web Worker runnnig environment, Gloria-X will create a virtual window
object to make some external scripts work normally, and the importScripts
method has been transformed into an async method, which only supports a single parameter. You can call it like this:
importScripts('script.js')
.then(script => {
...
})
When the async importScripts
method is called to load an external script, a Promise
object is returned. The first parameter received by the callback function of Promise.then
is the return value of the loaded external script.
Gloria-X and Gloria also have some commonly used modules built in, and these modules can be loaded through importScripts('gloria-utils')
. The available modules are shown in the following table:
Module | Version |
---|---|
export cheerio from 'cheerio' |
0.22.0 |
export co from 'co' |
4.6.0 |
export cookie from 'cookie' |
0.3.1 |
export immutable from 'immutable' |
3.8.1 |
export is from 'is_js' |
0.9.0 |
export lodash from 'lodash' |
4.16.4 |
export moment from 'moment' |
2.18.1 |
export qs from 'qs' |
6.3.0 |
export ramda from 'ramda' |
0.24.1 |
export rx from 'rx' |
4.1.0 |
export sanitizeHtml from 'sanitize-html' |
1.13.0 |
export SystemJS from 'systemjs' |
0.20.14 |
export underscoreString from 'underscore.string' |
3.3.4 |
export validator from 'validator' |
7.1.0 |
export xml2js from 'xml2js' |
0.4.17 |
export XRegExp from 'xregexp' |
3.2.0 |
For example, you can use cheerio
to parse a page:
(async () => {
const { cheerio } = await importScripts('gloria-utils');
const html = await fetch('https://github.com/LightAPIs/Gloria-X/releases').then(res => res.text());
const $ = cheerio.load(html);
return $('.release-main-section.commit')
.map((_i, ele) => {
const link = $(ele).find('.release-header .f1 a');
const title = link.text().trim();
const url = 'https://github.com/' + link.attr('href');
return {
title,
url,
};
})
.get();
})().then(commit);
v1.2.0 +
The feature can be found in the right-click menu of the web page or the right-click menu of the extension icon.
After triggering this feature, it will enter the mode of selecting web page elements. You can click on the elements in the web page with the left mouse button, hold down the Ctrl key while you can make multiple selections. After completing the element selection, click the "Generate" button to generate the corresponding task code. The next step is to test the task code, which executes successfully to create the task.
It is important to note that this feature is essentially a shortcut generator for Observation task. In principly, it can not generate General task, and it may not be able to monitor all types of web pages, such as dynacically loaded web pages based on Ajax technology.
The panel for debugging tasks can be found in the "Task debug" tab of the extension options page.
After entering the task code to be debugged in the code input box, click the "Testing" button at the top of the page to view the test output results (of course, the task code executed async may have to wait a while), and the test results obtained are both It does not pass through the internal STAGES component (a component used to cache notifications) or the Reducer function (see later introduction), but directly generates the corresponding notifications.
Errors such as syntax errors in the task code are also displayed in the panel (Note: It only supports capturing the errors of synchronous code, and the errors of asynchronous code or the output of 'console' statement in the code need to open the background page<"background.html"> to view)。
The panel for observing the internal state can be found in the "Internal state" tab of the extension options page.
Here, you can observe the real-time state of "Notification Records", "Gloria-X tasks" and "STAGES" components stored internally.
At the same time, buttons for cleaning up are provided at the bottom of the page.
v2.6.0+
The panel for Custom RequestHeaders can be found in the "RequestHeaders" tab of the extension options page.
Here, you can manually supply values for some message headers that Forbidden header name.
Note: The settings here override some of the same name values that are automatically attached within the extension, such as Cookie
.
The Reducer settings panel can be found in the "Reducer" tab of the extension options page.
If you need to perform some secondary operations on the new notifications obtained after the task is executed before being officially pushed, then you need to use the Reducer function. The Reducer function is a special function provided by Gloria-X. Unlike the commit
function in the previous task code, it does not appear in the task code, but is set separately in the options page. If you provide a Reducer function, then all tasks will use the same Reducer function.
Reducer is a synchronously executed function. When a new Gloria Notification
object needs to be pushed after a task is executed, the object will be passed as a parameter to the Reducer function to perform an operation.
Note: Before the Reducer function is executed, the comparison and caching of notifications have been completed. The operation of the Reducer function will only affect the content of the subsequent notifications and records.
Its execution timing is probably as follows:
flowchart LR
subgraph Execute
id1([Task]) --> id2[commit]
end
id2 --> id3
subgraph Compare and Cache
id3["Gloria Notification(s)"] --> id4[STAGES]
end
id4 --> id5{reducer exists?}
id5 -- No ----> id7[/Popup Notifications/]
id5 -- Yes --> id6[Reducer] --> id7
In the Reducer function, you can complete the operations of modification, filtering, and sending to third-party services (For example: Pushbullet、Pushover etc.) via HTTP requests to synchronize notifications to other devices.
The Reducer receives a Gloria Notification
object as a parameter, and optionally returns a new Gloria Notification
object. The return value will be used to pop up new notifications. If the function does not provide a return value or returns a null
, it is considered discarded, no new notification will pop up, and it will not be seen in the notification record.
Filter out notifications that contain the string "sad" in message
:
function reducer(notification) {
if (notification.message.includes('sad')) {
return null;
}
return notification;
}
Modify "sad" in message
to "happy":
function reducer(notification) {
notification.message = notification.message.replace('sad', 'happy');
return notification;
}
You can synchronize notification messages to other devices through Pushbullet. For more information on how to use Pushbullet, please refer to the official documentation.
function reducer(notification) {
const { title, message, url } = notification;
const data = {
type: 'note',
title,
body: message,
device_iden: '...', //? Optional, device id
};
url &&
Object.assign(data, {
type: 'link',
url,
});
fetch('https://api.pushbullet.com/v2/pushes', {
method: 'POST',
headers: new Headers({
'Access-Token': 'o.xxx', //! Your access token
'Content-Type': 'application/json',
}),
body: JSON.stringify(data),
});
return notification;
}
Here is the version using Pushover. For more interfaces of other tools, please refer to its documentation.
In the Reducer settings panel, you can also perform some simple tests on the Reducer functions you have written.
You can fill in a JSON string serialized by the Gloria Notification
object or its array in the test object input box. In fact, it directly simulates the result of a task execution. Then click the test button to view the return value of the Reducer function and the effect of the pop-up notification.
As for the code error of the Reducer function or the output of using the console
statement in the Reducer function, you need to open the background page<"background.html"> to view it.
Finally, I would like to remind you that you must use the Reducer function carefully.
There is an optional option of On-time mode
in the task, that is, the task performs the check strictly according to the given time interval.
If the task is enabled:
-
Tasks in the default mode will always be executed automatically when starting the browser, and then set a timer based on the active time of the moment.
-
Tasks that use the On-time mode will first determine whether the task has been executed since the last execution time exceeds the set execution interval when starting the browser. If it is still within the interval, the check will not be executed immediately, but according to the scheduled time The inspection will only be performed upon arrival.
- Allow implicit push notifications (record notifications but no news prompts)
- Support for later review of notifications
- Allow filtering tasks and notification records
- Allow custom notification to sound
- You can operate tasks and notification records in the Popup page through the right-click menu
For more specific details, please refer to:
- Gloria Chinese guide:https://docs.gloria.pub/
- Task code sharing website:https://gloria.pub/
There are some functional limitations in Firefox due to the different levels of API support available.
Here are some of the features that Firefox doesn’t support in comparison to Chrome:
- The pop-up notification needs to be manually closed as an optional option
- Display the image in the pop-up notifications
- Provide action buttons in pop-up notifications
Here are some logical differences in Firefox:
- If the "Show the number of unread notifications mark on the extension icon" option is enabled, when the close button of the notification is clicked, the value of the number mark will not decrease. (If the notification is associated with a URL, it will decrease when you click the notification to open the URL.)
- Install Node.js 16 (
npm
is integrated)
npm install
- Build the Chrome version:
npm run build-chrome
- Build the Edge version:
npm run build-edge
- Build the Firefox version:
npm run build-firefox
Here, I would like to express my heartfelt thanks to Gloria author BlackGlory and all related projects personnel. ❤️
- BlackGlory/Gloria
- akanshgulati/scrap-favicon
- CarterLi/vue3-ace-editor
- element-plus/element-plus
- GitHub-Laziji/menujs
- ReactiveX/rxjs
- Simonwep/selection
- vuejs/vue-next
- yyf1994gggg/vuex-chrome
MIT License