Added tasks runner and rss task
This commit is contained in:
parent
bf486a047b
commit
a24c352e8e
71
bot.js
71
bot.js
|
@ -2,14 +2,18 @@
|
|||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const irc = require("irc");
|
||||
|
||||
// Spells
|
||||
const Summon = require("./spells/summon");
|
||||
const BotList = require("./spells/botlist");
|
||||
const Banish = require("./spells/banish");
|
||||
const Pardon = require("./spells/pardon");
|
||||
const Hmm = require("./spells/hmm");
|
||||
|
||||
// Tasks
|
||||
const RSS = require("./tasks/rss");
|
||||
|
||||
const debug = false;
|
||||
|
||||
const botName = `BabiliBot${debug ? "Demo" : ""}`;
|
||||
|
@ -30,7 +34,7 @@ const client = new irc.Client("localhost", botName, {
|
|||
});
|
||||
|
||||
if (!debug) {
|
||||
client.opt.channels.push("#meta");
|
||||
client.opt.channels = client.opt.channels.concat(["#meta", "#tildeverse"]);
|
||||
}
|
||||
|
||||
client.config = JSON.parse(fs.readFileSync("config.json", "utf8"));
|
||||
|
@ -45,6 +49,41 @@ client.spells = [
|
|||
Hmm
|
||||
];
|
||||
|
||||
// NOTE - For my personal copy, jan6 will remain here until #chaos is mine
|
||||
client.blacklist = JSON.parse(fs.readFileSync(
|
||||
path.resolve(__dirname, "blacklist.json"), "utf8"
|
||||
));
|
||||
|
||||
// Memory log format:
|
||||
// {
|
||||
// timestamp: Date.now(),
|
||||
// spell: spell.name
|
||||
// }
|
||||
client.memory = {
|
||||
log: [],
|
||||
users: {},
|
||||
tasks: {}
|
||||
};
|
||||
|
||||
client.tasks = debug ? [] : [
|
||||
new RSS(
|
||||
client,
|
||||
"links",
|
||||
"https://links.tildeverse.org/newest.rss",
|
||||
"title",
|
||||
["#meta", "#tildeverse"],
|
||||
5000
|
||||
),
|
||||
new RSS(
|
||||
client,
|
||||
"links-comments",
|
||||
"https://links.tildeverse.org/comments.rss",
|
||||
"summary",
|
||||
["#tildeverse"],
|
||||
5000
|
||||
)
|
||||
];
|
||||
|
||||
client.triggers = {
|
||||
"!cast": {
|
||||
vars: " <spell>",
|
||||
|
@ -72,21 +111,7 @@ client.triggers = {
|
|||
// }
|
||||
};
|
||||
|
||||
// NOTE - For my personal copy, jan6 will remain here until #chaos is mine
|
||||
client.blacklist = JSON.parse(fs.readFileSync(
|
||||
path.resolve(__dirname, "blacklist.json"), "utf8"
|
||||
));
|
||||
|
||||
// Memory log format:
|
||||
// {
|
||||
// timestamp: Date.now(),
|
||||
// spell: spell.name
|
||||
// }
|
||||
client.memory = {
|
||||
log: [],
|
||||
users: {}
|
||||
};
|
||||
|
||||
// Brain of the bot
|
||||
client.brain = {};
|
||||
// Persistent memories
|
||||
client.brain.memories = JSON.parse(fs.readFileSync(
|
||||
|
@ -258,6 +283,8 @@ const runLogic = (client, from, to, message) => {
|
|||
|
||||
const tildebridge = new RegExp(/^<~?([^>]+)>\s/);
|
||||
|
||||
if (!message) return false;
|
||||
|
||||
// Handle bots
|
||||
if (from === "tildebridge") {
|
||||
const parsedMessage = message.split(tildebridge);
|
||||
|
@ -395,5 +422,11 @@ client.connect();
|
|||
|
||||
client.join("#bots");
|
||||
if (!debug) {
|
||||
client.join("#meta");
|
||||
}
|
||||
client.opt.channels.forEach((channel) => {
|
||||
client.join(channel);
|
||||
});
|
||||
}
|
||||
|
||||
client.tasks.forEach((task) => {
|
||||
task.start();
|
||||
});
|
|
@ -4,6 +4,11 @@
|
|||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"addressparser": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz",
|
||||
"integrity": "sha1-R6++GiqSYhkdtoOOT9HTm0CCF0Y="
|
||||
},
|
||||
"ajv": {
|
||||
"version": "5.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
|
||||
|
@ -15,6 +20,11 @@
|
|||
"json-schema-traverse": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"array-indexofobject": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/array-indexofobject/-/array-indexofobject-0.0.1.tgz",
|
||||
"integrity": "sha1-qqEo5iybPDWAlFaMIZ/2T+SJ1Co="
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
|
||||
|
@ -40,15 +50,6 @@
|
|||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz",
|
||||
"integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
|
||||
"integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.3.0",
|
||||
"is-buffer": "^1.1.5"
|
||||
}
|
||||
},
|
||||
"bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
|
@ -117,9 +118,9 @@
|
|||
}
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
|
||||
"integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||
},
|
||||
"extsprintf": {
|
||||
"version": "1.3.0",
|
||||
|
@ -136,22 +137,20 @@
|
|||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
|
||||
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.1.tgz",
|
||||
"integrity": "sha512-v9GI1hpaqq1ZZR6pBD1+kI7O24PhDvNGNodjS3MdcEqyrahCp8zbtpv+2B/krUnSmUH80lbAS7MrdeK5IylgKg==",
|
||||
"feedparser": {
|
||||
"version": "2.2.9",
|
||||
"resolved": "https://registry.npmjs.org/feedparser/-/feedparser-2.2.9.tgz",
|
||||
"integrity": "sha1-kTgZfa/a4F/K3eADa+6vYGbCxek=",
|
||||
"requires": {
|
||||
"debug": "^3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
}
|
||||
"addressparser": "^1.0.1",
|
||||
"array-indexofobject": "~0.0.1",
|
||||
"lodash.assign": "^4.2.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.has": "^4.5.2",
|
||||
"lodash.uniq": "^4.5.0",
|
||||
"mri": "^1.1.0",
|
||||
"readable-stream": "^2.2.2",
|
||||
"sax": "^1.2.4"
|
||||
}
|
||||
},
|
||||
"forever-agent": {
|
||||
|
@ -210,6 +209,11 @@
|
|||
"nan": "^2.3.5"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
},
|
||||
"irc": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/irc/-/irc-0.5.2.tgz",
|
||||
|
@ -225,16 +229,16 @@
|
|||
"resolved": "https://registry.npmjs.org/irc-colors/-/irc-colors-1.4.2.tgz",
|
||||
"integrity": "sha512-QZ1g4d9XTGKgBAp7lrltCetefqd3zfYs3SFQ4YyRSORORCmy/9EkU/r8LJrlSnaWc3Z+54EgHXBRlOHaCvpyHA=="
|
||||
},
|
||||
"is-buffer": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
|
||||
},
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
|
@ -277,24 +281,49 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
|
||||
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
|
||||
},
|
||||
"lodash.assign": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
|
||||
"integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc="
|
||||
},
|
||||
"lodash.get": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
|
||||
},
|
||||
"lodash.has": {
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz",
|
||||
"integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI="
|
||||
},
|
||||
"lodash.uniq": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
|
||||
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
|
||||
},
|
||||
"mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
|
||||
"integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="
|
||||
"version": "1.35.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz",
|
||||
"integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.18",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
|
||||
"integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
|
||||
"version": "2.1.19",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz",
|
||||
"integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==",
|
||||
"requires": {
|
||||
"mime-db": "~1.33.0"
|
||||
"mime-db": "~1.35.0"
|
||||
}
|
||||
},
|
||||
"mri": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.1.1.tgz",
|
||||
"integrity": "sha1-haom09ru7t+A3FmEr5XMXKXK2fE="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
|
@ -334,6 +363,11 @@
|
|||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
|
||||
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
|
||||
},
|
||||
"punycode": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||
|
@ -357,6 +391,20 @@
|
|||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"version": "2.87.0",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz",
|
||||
|
@ -412,6 +460,11 @@
|
|||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.14.2",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz",
|
||||
|
@ -433,6 +486,14 @@
|
|||
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
|
||||
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
|
||||
|
@ -463,6 +524,11 @@
|
|||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
||||
|
|
|
@ -19,9 +19,10 @@
|
|||
"author": "aewens",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"axios": "^0.18.0",
|
||||
"feedparser": "^2.2.9",
|
||||
"irc": "^0.5.2",
|
||||
"nodemailer": "^4.6.7",
|
||||
"pushbullet": "^2.2.0"
|
||||
"pushbullet": "^2.2.0",
|
||||
"request": "^2.87.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
const request = require("request");
|
||||
const FeedParser = require("feedparser");
|
||||
|
||||
// For pushing updates from https://links.tildeverse.org
|
||||
module.exports = class RSS {
|
||||
constructor(client, alias, source, use, channels, interval) {
|
||||
this.name = "RSS";
|
||||
this.client = client;
|
||||
this.alias = alias;
|
||||
this.source = source;
|
||||
this.use = use;
|
||||
this.channels = channels;
|
||||
this.interval = interval;
|
||||
|
||||
const task_memories = this.client.memory.tasks;
|
||||
task_memories[this.alias] = task_memories[this.alias] || {};
|
||||
this.memory = task_memories[this.alias];
|
||||
}
|
||||
|
||||
start() {
|
||||
this.memory.status = "running";
|
||||
this.memory.timestamp = Date.now();
|
||||
this.memory.known = [];
|
||||
this.fetch(this.cache.bind(this));
|
||||
this.job = setInterval(this.run.bind(this), this.interval);
|
||||
}
|
||||
|
||||
stop(ack) {
|
||||
clearInterval(this.job);
|
||||
client.say(ack, `Stopping task "${this.alias}"`);
|
||||
}
|
||||
|
||||
run() {
|
||||
// this.fetch(console.log.bind(console));
|
||||
this.fetch(this.mirror.bind(this));
|
||||
}
|
||||
|
||||
cache(item) {
|
||||
this.memory.known.push(item.guid);
|
||||
}
|
||||
|
||||
mirror(item) {
|
||||
if (this.memory.known.indexOf(item.guid) >= 0) return;
|
||||
|
||||
this.memory.known.push(item.guid);
|
||||
|
||||
const use = item[this.use].replace(/(<\/?[^>]+>)|\\n/g, "");
|
||||
const post = `${use} <${item.guid}>`;
|
||||
const response = `[${this.alias}] New submission: ${post}`;
|
||||
this.channels.forEach((channel) => {
|
||||
this.client.say(channel, response);
|
||||
});
|
||||
}
|
||||
|
||||
fetch(callback) {
|
||||
const req = request(this.source);
|
||||
const parser = new FeedParser();
|
||||
|
||||
req.on("error", (error) => {
|
||||
console.error(`${this.name}::error: [request] ${error}`);
|
||||
});
|
||||
|
||||
req.on("response", (res) => {
|
||||
const status = res.statusCode;
|
||||
|
||||
if (status !== 200) {
|
||||
req.emit("error", new Error(`Bad status code (${status})`));
|
||||
} else {
|
||||
req.pipe(parser);
|
||||
}
|
||||
});
|
||||
|
||||
parser.on("error", (error) => {
|
||||
console.error(`${this.name}::error: [request] ${error}`);
|
||||
});
|
||||
|
||||
parser.on("readable", () => {
|
||||
let item = parser.read();
|
||||
let meta = parser.meta;
|
||||
|
||||
while (item) {
|
||||
callback(item);
|
||||
item = parser.read();
|
||||
}
|
||||
});
|
||||
|
||||
parser.on("end", () => {
|
||||
this.memory.timestamp = Date.now();
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue