diff --git a/.eslintrc.js b/.eslintrc.js index 9720b2b..4b612ec 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -9,6 +9,7 @@ module.exports = { "sourceType": "module" }, "rules": { + "no-console": "off", "indent": ["error", 4], "indent": ["warn", 2], "linebreak-style": [ diff --git a/package-lock.json b/package-lock.json index 0cffed9..74a26ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -678,6 +678,29 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "aws-sdk": { + "version": "2.512.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.512.0.tgz", + "integrity": "sha512-ioZZ6dTiZyETpJJ3aYy81OS3idnU5n8eyFssvgMXdsX8PRJ3KJZ/dQAdCD/680xDt3ymRNClGvlkPcOLPNlDKQ==", + "requires": { + "buffer": "4.9.1", + "events": "1.1.1", + "ieee754": "1.1.8", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.3.2", + "xml2js": "0.4.19" + }, + "dependencies": { + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + } + } + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -1369,6 +1392,21 @@ "node-int64": "^0.4.0" } }, + "bson": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.1.tgz", + "integrity": "sha512-jCGVYLoYMHDkOsbwJZBCqwMHyH4c+wzgI9hG7Z6SZJRXWr+x58pdIbm2i9a/jFGCkRJqRUr8eoI7lDWa0hTkxg==" + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -2666,6 +2704,11 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, "exec-sh": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz", @@ -4722,6 +4765,11 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -6142,6 +6190,11 @@ "merge-stream": "^1.0.1" } }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -6294,6 +6347,11 @@ "safe-buffer": "^5.0.1" } }, + "kareem": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.0.tgz", + "integrity": "sha512-6hHxsp9e6zQU8nXsP+02HGWXwTkOEw6IROhF2ZA28cYbUk4eJ6QbtZvdqZOdD9YPKghG3apk5eOCvs+tLl3lRg==" + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -6559,6 +6617,12 @@ "mimic-fn": "^1.0.0" } }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, "merge": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", @@ -6673,6 +6737,93 @@ "minimist": "0.0.8" } }, + "mongodb": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.2.7.tgz", + "integrity": "sha512-2YdWrdf1PJgxcCrT1tWoL6nHuk6hCxhddAAaEh8QJL231ci4+P9FLyqopbTm2Z2sAU6mhCri+wd9r1hOcHdoMw==", + "requires": { + "mongodb-core": "3.2.7", + "safe-buffer": "^5.1.2" + } + }, + "mongodb-core": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.2.7.tgz", + "integrity": "sha512-WypKdLxFNPOH/Jy6i9z47IjG2wIldA54iDZBmHMINcgKOUcWJh8og+Wix76oGd7EyYkHJKssQ2FAOw5Su/n4XQ==", + "requires": { + "bson": "^1.1.1", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + } + }, + "mongoose": { + "version": "5.6.9", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.6.9.tgz", + "integrity": "sha512-NRW5UJSmwyJxK+MRHmq+dQKgZqMZCpW1aPkpBZESqrrgF2J15Flo/4K3RYkSSQY7oKhfbgqZTPo+J1snJ3hJ3w==", + "requires": { + "async": "2.6.2", + "bson": "~1.1.1", + "kareem": "2.3.0", + "mongodb": "3.2.7", + "mongodb-core": "3.2.7", + "mongoose-legacy-pluralize": "1.0.2", + "mpath": "0.6.0", + "mquery": "3.2.1", + "ms": "2.1.2", + "regexp-clone": "1.0.0", + "safe-buffer": "5.1.2", + "sift": "7.0.1", + "sliced": "1.0.1" + }, + "dependencies": { + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "requires": { + "lodash": "^4.17.11" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "mongoose-legacy-pluralize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" + }, + "mpath": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.6.0.tgz", + "integrity": "sha512-i75qh79MJ5Xo/sbhxrDrPSEG0H/mr1kcZXJ8dH6URU5jD/knFxCVqVC/gVSW7GIXL/9hHWlT9haLbCXWOll3qw==" + }, + "mquery": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.1.tgz", + "integrity": "sha512-kY/K8QToZWTTocm0U+r8rqcJCp5PRl6e8tPmoDs5OeSO3DInZE2rAL6AYH+V406JTo8305LdASOQcxRDqHojyw==", + "requires": { + "bluebird": "3.5.1", + "debug": "3.1.0", + "regexp-clone": "^1.0.0", + "safe-buffer": "5.1.2", + "sliced": "1.0.1" + }, + "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" + } + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -7799,6 +7950,11 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, "randomatic": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz", @@ -7972,6 +8128,11 @@ "safe-regex": "^1.1.0" } }, + "regexp-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", + "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" + }, "regexpp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz", @@ -8114,6 +8275,22 @@ "resolve-from": "^1.0.0" } }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + }, + "dependencies": { + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + } + } + }, "resolve": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", @@ -8555,11 +8732,19 @@ } } }, + "saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "semver": { "version": "5.5.1", @@ -8674,6 +8859,11 @@ "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", "dev": true }, + "sift": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", + "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==" + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -8700,6 +8890,11 @@ "is-fullwidth-code-point": "^2.0.0" } }, + "sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" + }, "snakeize": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/snakeize/-/snakeize-0.1.0.tgz", @@ -8848,6 +9043,15 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "requires": { + "memory-pager": "^1.0.2" + } + }, "spdx-correct": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", @@ -9654,6 +9858,22 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, "url-parse-lax": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", @@ -9935,6 +10155,20 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + }, "xmlhttprequest": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", diff --git a/package.json b/package.json index 371a918..3e91484 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "author": "", "dependencies": { "@slack/web-api": "^5.0.1", + "aws-sdk": "^2.512.0", "body-parser": "^1.18.3", "cheerio": "^1.0.0-rc.2", "cors": "^2.8.4", @@ -22,6 +23,7 @@ "dotenv": "^6.0.0", "express": "^4.16.3", "firebase-admin": "^7.3.0", + "mongoose": "^5.6.9", "node-cron": "^2.0.3", "redis": "^2.8.0", "request": "^2.88.0", diff --git a/src/controllers/notes.js b/src/controllers/notes.js new file mode 100644 index 0000000..e3d0db9 --- /dev/null +++ b/src/controllers/notes.js @@ -0,0 +1,62 @@ +/* eslint-disable no-console */ +import { Notes } from '../core/mongo' +import { getS3file } from '../core/aws' +import { SEMESTERS, BRANCHES, SUBJECTS } from '../utils/constants' + +export const showNotes = async (req, res) => { + try { + const { subject, module } = req.body + const notes = await Notes.find({ subject: subject, module: module }) + const note_data = await Promise.all(notes.map(async (note) => { + const url = await getS3file('cse/s2/coa/1/note1.pdf') + return { note_details: note, url: url } + })) + res.json({ + notes: note_data + }) + } catch (err) { + console.error(err) + } +} + +export const addNote = async (req, res) => { + try { + const { subject, module, link } = req.body + const note_data = { + subject, + module, + link, + } + const notes = new Notes(note_data) + await notes.save() + res.json({ status: 'Saved successfully' }) + } catch (err) { + console.error(err) + } +} + +export const showSemesters = async (req, res) => { + try { + res.json({ semesters: SEMESTERS }) + } catch (err) { + console.error(err) + } +} + +export const showBranches = async (req, res) => { + try { + res.json({ branches: BRANCHES }) + } catch (err) { + console.error(err) + } +} + +export const showSubjects = async (req, res) => { + try { + const { semester, branch } = req.body + const subjects = SUBJECTS.filter((s) => (s.semester === semester && s.branch === branch)) + res.json({ subjects: subjects }) + } catch (err) { + console.error(err) + } +} diff --git a/src/controllers/notifications.js b/src/controllers/notifications.js index 4877248..8298322 100644 --- a/src/controllers/notifications.js +++ b/src/controllers/notifications.js @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ import { getNotifications } from '../utils/redis' const showNotifications = async (req, res) => { diff --git a/src/core/aws.js b/src/core/aws.js new file mode 100644 index 0000000..33df370 --- /dev/null +++ b/src/core/aws.js @@ -0,0 +1,19 @@ +import aws from 'aws-sdk' +require('dotenv').config() + +const s3 = new aws.S3() +aws.config.region = 'us-east-1' + +const Bucket = process.env.S3_BUCKET + +export const getS3file = (key) => { + + const params = { Bucket, Key: key, Expires: 20 } + + return new Promise((resolve, reject) => { + s3.getSignedUrl('getObject', params, (err, url) => { + if (err) reject(err) + else resolve(url) + }) + }) +} diff --git a/src/core/mongo.js b/src/core/mongo.js new file mode 100644 index 0000000..59670c3 --- /dev/null +++ b/src/core/mongo.js @@ -0,0 +1,16 @@ +/* eslint-disable no-console */ +import mongoose from 'mongoose' +import noteSchema from '../schema/note' +require('dotenv').config() + +mongoose.connect(process.env.MONGO_URL, {useNewUrlParser: true}) + +mongoose.connection.on('error', (error) => { + console.error(error) +}) + +mongoose.connection.once('open', () => { + console.log('connected to mongo') +}) + +export const Notes = mongoose.model('notes', noteSchema) diff --git a/src/jobs/notification.js b/src/jobs/notification.js index 32384a2..cb89d19 100644 --- a/src/jobs/notification.js +++ b/src/jobs/notification.js @@ -5,7 +5,6 @@ import { saveNotifications } from '../utils/redis' import { sendMessage } from '../core/slack.js' const fetchNotifications = async () => { - console.log("Fetching Notifications") sendMessage('Fetching Notifications') const options = { @@ -17,7 +16,6 @@ const fetchNotifications = async () => { try { const response = await rp(options) const notifications = parseNotifications(response) - console.log("Fetching Notifications", notifications) saveNotifications(notifications) } catch (e) { console.error(e) diff --git a/src/routes/index.js b/src/routes/index.js index d8e5b00..f33dd3e 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -1,6 +1,7 @@ import { Router } from 'express' import getStudentDetails from '../controllers/studentDetails' import getNotifications from '../controllers/notifications' +import { showNotes, showBranches, showSemesters, showSubjects, addNote } from '../controllers/notes' import { validateRequest } from '../middleware' const router = Router() @@ -8,5 +9,10 @@ const router = Router() router.get('/', (req, res) => res.json({ status: 'working' })) router.post('/api/v1/data', validateRequest, getStudentDetails) router.get('/api/v1/notifications', getNotifications) +router.post('/api/v1/notes/subjects', showSubjects) +router.post('/api/v1/notes/semesters', showSemesters) +router.post('/api/v1/notes/branches', showBranches) +router.post('/api/v1/notes/notes', showNotes) +router.post('/api/v1/notes.save', addNote) export default router diff --git a/src/schema/note.js b/src/schema/note.js new file mode 100644 index 0000000..19c90d1 --- /dev/null +++ b/src/schema/note.js @@ -0,0 +1,9 @@ +import { Schema } from 'mongoose' + +const noteSchema = new Schema({ + subject: 'String', + module: 'Number', + link: 'String' +}) + +export default noteSchema diff --git a/src/utils/constants.js b/src/utils/constants.js new file mode 100644 index 0000000..e057f1c --- /dev/null +++ b/src/utils/constants.js @@ -0,0 +1,23 @@ +export const SEMESTERS = { + s1_2: 'Semester 1 & 2', + s3: 'Semester 3', + s4: 'Semester 4', + s5: 'Semester 5', + s6: 'Semester 6', + s7: 'Semester 7', + s8: 'Semester 8' +} + +export const BRANCHES = { + cse: 'CSE' +} + +export const SUBJECTS = [ + { + key: 'coa', + name: 'Computer Organisation and Architecture', + semester: SEMESTERS.s3, + branch: BRANCHES.cse, + modules: 4 + }, +] diff --git a/src/utils/slugify.js b/src/utils/slugify.js index df01d07..08fc646 100644 --- a/src/utils/slugify.js +++ b/src/utils/slugify.js @@ -6,8 +6,8 @@ export default (string) => { .replace(/\s+/g, '-') .replace(p, c => b.charAt(a.indexOf(c))) .replace(/&/g, '-and-') - .replace(/[^\w\-]+/g, '') - .replace(/\-\-+/g, '-') + .replace(/[^\w-]+/g, '') + .replace(/--+/g, '-') .replace(/^-+/, '') .replace(/-+$/, '') }