{"version":3,"sources":["jsdelivr-header.js","/npm/kijiji-fb-bot@2.0.0/index.js"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA,ACHA,IAAI,GAAK,QAAQ,MACb,OAAS,QAAQ,kBACjB,GAAK,QAAQ,qBACb,IAAM,QAAQ,kBAEd,KAAO,KAEP,MAAQ,CACR,SAAY,CAAC,EACb,QAAW,CAAC,EACZ,aAAgB,CAAC,EACjB,SAAW,EACX,YAAe,KACf,QAAW,GACX,eAAkB,IAAI,KAAK,IAI3B,aAAe,SAAS,EAAW,GACnC,QAAQ,IAAI,2BAGZ,OAAO,MAAM,MAAM,QAAS,MAAM,cAAc,SAAS,EAAK,GAC1D,GAAK,EAeD,QAAQ,MAAM,0BAA4B,GAC1C,EAAS,EAAK,UAhBR,CACN,OAAS,GAGT,IAAK,IAAI,EAAE,EAAG,EAAI,EAAI,OAAQ,IACtB,EAAI,GAAG,eAAe,YAClB,IAAI,KAAK,EAAI,GAAG,YAAc,GAC9B,OAAO,KAAK,EAAI,IAK5B,QAAQ,IAAI,OAAS,OAAO,OAAS,gBACrC,EAAS,EAAK,OAClB,CAIJ,GACJ,EAGI,WAAa,SAAS,EAAQ,EAAK,EAAQ,GAG3C,IAFA,IAAI,EAAS,GAAK,EAET,EAAE,EAAG,EAAI,EAAI,OAAQ,IAC1B,GAAU,OAAO,MAAM,EAAI,IAAM,OAErC,KAAK,YAAY,EAAQ,EAAQ,EACrC,EAGI,WAAa,SAAS,EAAQ,GAC9B,QAAQ,IAAI,6BACZ,KAAK,YAAY,gBAAiB,EAAQ,GAE1C,aAAa,MAAM,gBAAgB,SAAS,EAAK,GAC7C,GAAK,EAOD,KAAK,YAAY,mCAAoC,EAAQ,OAPvD,CACN,MAAM,QAAU,EAChB,MAAM,eAAiB,IAAI,KAE3B,IAAI,EAAS,OAAS,MAAM,QAAQ,OAAS,mCAC7C,WAAW,EAAQ,MAAM,QAAS,EACtC,CAGJ,GACJ,EAGI,SAAW,SAAS,EAAQ,GAC5B,QAAQ,IAAI,uBAEZ,IAAI,EAAO,sBAAwB,IAAI,QAA5B,wBACkB,MAAM,eADxB,iCAE+B,MAAM,QAAQ,OAAS,OAIjE,IAAK,IAAI,KADT,GAAQ,iCACS,MAAM,SACf,MAAM,SAAS,eAAe,KAC9B,GAAQ,EAAO,KAAO,MAAM,SAAS,GAAQ,QAMrD,IAAK,IAAI,KADT,GAAQ,iCACS,MAAM,QACf,MAAM,QAAQ,eAAe,KAC7B,GAAQ,EAAO,KAAO,MAAM,QAAQ,GAAQ,QAMpD,IAAK,IAAI,KADT,GAAQ,oCACS,MAAM,aACf,MAAM,aAAa,eAAe,KAClC,GAAQ,EAAO,KAAO,MAAM,aAAa,GAAQ,QAIzD,KAAK,YAAY,EAAM,EAAQ,EACnC,EAGI,SAAW,SAAS,EAAQ,GAC5B,QAAQ,IAAI,uBAEZ,IAAI,EAAO,WAAa,MAAM,SAAS,KAA5B,+UASP,MAAM,SAAS,cACf,GAAQ,8LAKZ,GAAQ,kDAER,KAAK,YAAY,EAAM,EAAQ,EACnC,EAGI,QAAU,SAAS,EAAS,EAAM,EAAK,EAAQ,GAC/C,IAAI,EAGJ,GAAK,EAAQ,eAAe,GAIvB,GAAK,EAIL,CACD,QAAQ,IAAI,iCAEZ,IAAI,EAAS,EAAQ,GAAM,YAAY,GACvC,EAAW,sBAAwB,EAAO,iBAAmB,EAAS,IACtE,EAAQ,GAAQ,EAGH,mBAAT,GACA,eAAe,MAAM,YAAa,gBAAiB,MAAM,SAAS,eAC1E,MAbI,EAAW,sBAAwB,EAAO,SAAW,EAAQ,GAAQ,SAJrE,EAAW,aAAe,EAAO,cAmBrC,KAAK,YAAY,EAAU,EAAQ,EACvC,EAGI,mBAAqB,SAAS,EAAS,EAAQ,GAC/C,KAAK,YAAY,oBAAsB,EAAU,IAAK,EAAQ,EAClE,EAGI,eAAiB,SAAS,EAAU,EAAM,GAC1C,cAAc,GACd,EAAW,GAAW,EAAI,YAAY,EAAM,GAAW,IAC3D,EAGI,gBAAkB,WAClB,KAAK,YAAY,iCAAkC,MAAM,SAAS,QAClE,WAAW,MAAM,SAAS,QAC1B,eAAe,MAAM,YAAa,gBAAiB,MAAM,SAAS,eACtE,EAGI,aAAe,SAAS,EAAK,EAAK,GAClC,GAAI,EAAK,OAAO,QAAQ,MAAM,GAC9B,IAAK,MAAM,QAAS,OAAO,IAG3B,GAAoD,IAAhD,EAAI,KAAK,QAAQ,MAAM,SAAS,KAAO,KAAY,CACnD,IAAI,EAAQ,EAAI,KAAK,MAAM,MAAM,SAAS,KAAO,KAAK,GAAG,MAAM,KAC3D,EAAU,EAAM,GAChB,EAAO,EAAM,MAAM,EAAG,EAAM,QAAQ,KAAK,KAK7C,GAAI,MAAM,SAAS,YAAa,CAC5B,IAAI,EAAS,EAAK,MAAM,KACpB,EAAO,EAAO,GACd,EAAM,EAAO,MAAM,EAAG,EAAO,QAAQ,KAAK,KAE9C,GAAgB,YAAZ,EACA,OAAO,QAAQ,MAAM,SAAU,EAAM,EAAK,EAAI,WAC7C,GAAgB,WAAZ,EACL,OAAO,QAAQ,MAAM,QAAS,EAAM,EAAK,EAAI,WAC5C,GAAgB,gBAAZ,EACL,OAAO,QAAQ,MAAM,aAAc,EAAM,EAAK,EAAI,UAC1D,CAEgB,SAAZ,EACA,WAAW,2BAA6B,MAAM,QAAQ,OAAS,YACpD,MAAM,QAAS,EAAI,WACb,WAAZ,EACL,WAAW,EAAI,WACE,SAAZ,EACL,SAAS,EAAI,WACI,SAAZ,EACL,SAAS,EAAI,WAEb,mBAAmB,EAAS,EAAI,UACxC,CACJ,EAGI,KAAO,WACP,MAAM,SAAU,EAChB,cAAc,MAAM,aACpB,QAAQ,IAAI,OAAS,MAAM,SAAS,KAAO,eAC/C,EAGI,KAAO,SAAS,EAAW,GAC3B,QAAQ,IAAI,2BAEZ,IAAI,EAAQ,KAAK,MAAM,GAAG,aAAa,EAAY,mBAGnD,MAAM,SAAW,KAAK,MAAM,GAAG,aAAa,EAAY,mBACxD,MAAM,QAAU,KAAK,MAAM,GAAG,aAAa,EAAY,kBACvD,MAAM,aAAe,KAAK,MAAM,GAAG,aAAa,EAAY,uBAE5D,MAAM,SAAU,EAGhB,GAAG,GAAO,SAAS,EAAK,GACpB,GAAI,EAGA,OAFA,MAAM,SAAU,EAChB,QAAQ,MAAM,0BAA4B,GACnC,EAAS,EAAK,MAEzB,EAAI,WAAW,CAAC,SAAU,SAC1B,EAAI,OAAO,cAEX,KAAO,EACP,QAAQ,IAAI,OAAS,MAAM,SAAS,KAAO,iBAEvC,MAAM,SAAS,eAAe,mBAAqB,MAAM,SAAS,gBAAkB,IACpF,MAAM,YAAc,YAAY,gBAAiB,MAAM,SAAS,iBAEpE,EAAS,KAAM,KACnB,GACJ,EAEA,OAAO,QAAU","file":"/npm/kijiji-fb-bot@2.0.0/index.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/kijiji-fb-bot@2.0.0/index.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","//index.js\r\n/*A bot for checking recent Kijiji ads and notifying a group chat*/\r\n\r\nvar fs = require(\"fs\");\r\nvar kijiji = require(\"kijiji-scraper\");\r\nvar fb = require(\"facebook-chat-api\");\r\nvar pkg = require(\"./package.json\");\r\n\r\nvar chat = null;\r\n\r\nvar state = {\r\n    \"botProps\": {},\r\n    \"adPrefs\": {},\r\n    \"searchParams\": {},\r\n    \"running\": false,\r\n    \"scrapeTimer\": null,\r\n    \"lastAds\": [],\r\n    \"lastScrapeDate\": new Date(0)\r\n};\r\n\r\n/*Scrapes for ads newer than the start date*/\r\nvar scrapeNewAds = function(startDate, callback) {\r\n    console.log(\"[+] Scraping new ads...\");\r\n\r\n    //Search Kijiji\r\n    kijiji.query(state.adPrefs, state.searchParams, function(err, ads) {    \r\n        if (!err) {\r\n            tmpAds = [];\r\n\r\n            //Only add ads newer than the start date\r\n            for (var i=0; i < ads.length; i++) {\r\n                if (ads[i].hasOwnProperty(\"dc:date\")) {\r\n                    if (new Date(ads[i][\"dc:date\"]) > startDate) {\r\n                        tmpAds.push(ads[i]);\r\n                    }\r\n                }\r\n            }\r\n\r\n            console.log(\"[+] \" + tmpAds.length + \" ads scraped\");\r\n            callback(err, tmpAds);\r\n        } else {\r\n            console.error(\"[-] Error scraping ads \" + err);\r\n            callback(err, null);\r\n        }\r\n    });\r\n};\r\n\r\n/*Sends a list of ads to the chat*/\r\nvar sendAdList = function(header, ads, thread, callback) {\r\n    var output = \"\" + header;\r\n\r\n    for (var i=0; i < ads.length; i++) {\r\n        output += kijiji.parse(ads[i]) + \"\\r\\n\";\r\n    }\r\n    chat.sendMessage(output, thread, callback);\r\n};\r\n\r\n/*Searches Kijiji for matching recent ads sends them to the given FB chat thread*/\r\nvar sendNewAds = function(thread, callback) {\r\n    console.log(\"[+] Sending recent ads...\");\r\n    chat.sendMessage(\"One moment...\", thread, callback); //this could take a while\r\n    \r\n    scrapeNewAds(state.lastScrapeDate, function(err, ads) {\r\n        if (!err) {\r\n            state.lastAds = ads;\r\n            state.lastScrapeDate = new Date();\r\n            \r\n            var header = \"~~~ \" + state.lastAds.length + \" ad(s) since last scrape ~~~\\r\\n\";            \r\n            sendAdList(header, state.lastAds, thread);\r\n        } else {\r\n            chat.sendMessage(\"Error scraping ads (see console)\", thread, callback);\r\n        }\r\n    });\r\n};\r\n\r\n/*Sends bot state info to the chat*/\r\nvar sendInfo = function(thread, callback) {\r\n    console.log(\"[+] Sending info...\");\r\n\r\n    var info = \"~~~ kijiji-fb-bot v\" + pkg.version + \" ~~~\\r\\n\" +\r\n               \"Last scrape: \" + state.lastScrapeDate + \"\\r\\n\" +\r\n               \"Ads found at last scrape: \" + state.lastAds.length + \"\\r\\n\";\r\n    \r\n    //Add bot properties to output\r\n    info += \"\\r\\n~~~ bot properties ~~~\\r\\n\";\r\n    for (var prop in state.botProps) {\r\n        if (state.botProps.hasOwnProperty(prop)) {\r\n            info += prop + \": \" + state.botProps[prop] + \"\\r\\n\";\r\n        }\r\n    }\r\n\r\n    //Add ad preferences to output\r\n    info += \"\\r\\n~~~ ad preferences ~~~\\r\\n\";\r\n    for (var prop in state.adPrefs) {\r\n        if (state.adPrefs.hasOwnProperty(prop)) {\r\n            info += prop + \": \" + state.adPrefs[prop] + \"\\r\\n\";\r\n        }\r\n    }\r\n\r\n    //Add search parameters to output\r\n    info += \"\\r\\n~~~ search parameters ~~~\\r\\n\";\r\n    for (var prop in state.searchParams) {\r\n        if (state.searchParams.hasOwnProperty(prop)) {\r\n            info += prop + \": \" + state.searchParams[prop] + \"\\r\\n\";\r\n        }\r\n    }\r\n\r\n    chat.sendMessage(info, thread, callback);\r\n};\r\n\r\n/*Sends a help/syntax message*/\r\nvar sendHelp = function(thread, callback) {\r\n    console.log(\"[+] Sending help...\");\r\n\r\n    var help = \"Hi, I'm \" + state.botProps.name + \": the helpful ad-scraping robot!\\r\\n\" +\r\n               \"I have a have a few useful commands you can use.\\r\\n\" +\r\n               \"Prefix a command with my name, so I know you're talking to me.\\r\\n\\r\\n\" +\r\n               \"Commands:\\r\\n\" +\r\n               \"\\tlist\\t\\t\\t\\t\\tGets a list of the last scraped ads.\\r\\n\" +\r\n               \"\\tscrape\\t\\t\\t\\tGets ads posted since the last scrape.\\r\\n\" +\r\n               \"\\tinfo\\t\\t\\t\\t\\tGets bot state information.\\r\\n\";\r\n\r\n    //Only show property-changing commands if they're allowed\r\n    if (state.botProps.remoteAdmin) {\r\n        help += \"\\tbotprop [prop] [val]\\t\\tGets/sets a bot property.\\r\\n\" +\r\n                \"\\tadpref [pref] [val]\\t\\t\\tGets/sets an ad search preference.\\r\\n\" +\r\n                \"\\tsearchparam [param] [val]\\tGets/sets an ad search parameter.\\r\\n\";\r\n    }\r\n\r\n    help += \"\\thelp\\t\\t\\t\\t\\tDisplays this help information.\";\r\n\r\n    chat.sendMessage(help, thread, callback);\r\n};\r\n\r\n/*Gets/sets a property of an object*/\r\nvar modProp = function(propObj, prop, val, thread, callback) {\r\n    var response;\r\n\r\n    //Prop not valid\r\n    if (!propObj.hasOwnProperty(prop)) {\r\n        response = \"Property '\" + prop + \"' not found\";\r\n    }\r\n    //No value to set -- return the current value\r\n    else if (!val) {\r\n        response = \"Value of property '\" + prop + \"' is '\" + propObj[prop] + \"'\";\r\n    }\r\n    //Set a new value\r\n    else {\r\n        console.log(\"[+] Sending property value...\");\r\n\r\n        var newVal = propObj[prop].constructor(val); //want new val to be same type as old\r\n        response = \"Value of property '\" + prop + \"' changed to '\" + newVal + \"'\";\r\n        propObj[prop] = newVal;\r\n        \r\n        //Update the scrape timer if the interval was changed\r\n        if (prop === \"scrapeInterval\")\r\n            updateInterval(state.scrapeTimer, scheduledScrape, state.botProps.scrapeInterval);\r\n    }\r\n\r\n    chat.sendMessage(response, thread, callback);\r\n}\r\n\r\n/*Sends an error for a wrong command*/\r\nvar sendUnknownCommand = function(command, thread, callback) {\r\n    chat.sendMessage(\"Unknown command '\" + command + \"'\", thread, callback);\r\n};\r\n\r\n/*Updates an interval with a new timeout*/\r\nvar updateInterval = function(interval, func, timeout) {\r\n    clearInterval(interval);\r\n    interval = timeout >= 0 ? setInterval(func, timeout) : null;\r\n};\r\n\r\n/*The function called every scrape interval*/\r\nvar scheduledScrape = function() {\r\n    chat.sendMessage(\"Performing scheduled scrape...\", state.botProps.chatId);\r\n    sendNewAds(state.botProps.chatId);\r\n    updateInterval(state.scrapeTimer, scheduledScrape, state.botProps.scrapeInterval);\r\n};\r\n\r\n/*Listens for bot commands*/\r\nvar chatListener = function(err, msg, stopListening) {\r\n    if (err) return console.error(err);\r\n    if (!state.running) return stopListening();\r\n    \r\n    //Commands must start with the bot's name\r\n    if (msg.body.indexOf(state.botProps.name + \" \") === 0) {\r\n        var query = msg.body.split(state.botProps.name + \" \")[1].split(\" \");    \r\n        var command = query[0];\r\n        var args = query.slice(1, query.length).join(\" \");\r\n\r\n        /*Command handlers*/\r\n\r\n        //Only check for property-changing commands if they're allowed\r\n        if (state.botProps.remoteAdmin) {\r\n            var argArr = args.split(\" \");\r\n            var prop = argArr[0];\r\n            var val = argArr.slice(1, argArr.length).join(\" \");\r\n\r\n            if (command === \"botprop\")\r\n                return modProp(state.botProps, prop, val, msg.thread_id);\r\n            else if (command === \"adpref\")\r\n                return modProp(state.adPrefs, prop, val, msg.thread_id);\r\n            else if (command === \"searchparam\")\r\n                return modProp(state.searchParams, prop, val, msg.thread_id);\r\n        }\r\n\r\n        if (command === \"list\")\r\n            sendAdList(\"~~~ last ad(s) scraped (\" + state.lastAds.length + \") ~~~\\r\\n\",\r\n                       state.lastAds, msg.thread_id);\r\n        else if (command === \"scrape\")\r\n            sendNewAds(msg.thread_id);  \r\n        else if (command === \"info\")\r\n            sendInfo(msg.thread_id);\r\n        else if (command === \"help\")\r\n            sendHelp(msg.thread_id);\r\n        else\r\n            sendUnknownCommand(command, msg.thread_id);\r\n    }\r\n}\r\n\r\n/*Sets the bot to stop after the next message is received*/\r\nvar stop = function() {\r\n    state.running = false;\r\n    clearInterval(state.scrapeTimer);\r\n    console.log(\"[+] \" + state.botProps.name + \" set to stop\");\r\n}\r\n\r\n/*Initializes the bot*/\r\nvar init = function(configDir, callback) {\r\n    console.log(\"[+] Initializing bot...\");\r\n\r\n    var creds = JSON.parse(fs.readFileSync(configDir + \"/facebook.json\"));\r\n\r\n    //Load preferences\r\n    state.botProps = JSON.parse(fs.readFileSync(configDir + \"/botprops.json\"));\r\n    state.adPrefs = JSON.parse(fs.readFileSync(configDir + \"/adprefs.json\"));\r\n    state.searchParams = JSON.parse(fs.readFileSync(configDir + \"/searchparams.json\"));\r\n\r\n    state.running = true;\r\n\r\n    //Start listening in the chat\r\n    fb(creds, function(err, api) {\r\n        if (err) {\r\n            state.running = false;\r\n            console.error(\"[-] Error starting bot \" + err);\r\n            return callback(err, null);\r\n        }\r\n        api.setOptions({logLevel: \"warn\"});\r\n        api.listen(chatListener);\r\n        \r\n        chat = api;\r\n        console.log(\"[+] \" + state.botProps.name + \" is listening\");\r\n        \r\n        if (state.botProps.hasOwnProperty(\"scrapeInterval\") && state.botProps.scrapeInterval >= 0)\r\n            state.scrapeTimer = setInterval(scheduledScrape, state.botProps.scrapeInterval);\r\n        \r\n        callback(null, stop);\r\n    });\r\n};\r\n\r\nmodule.exports = init;\r\n"]}