intel-screenbot.meta

Issues/PRs archive for MTRNord/intel_screenbot
git clone git://archive.git.mtrnord.blog/MTRNord/intel-screenbot.meta.git
Log | Files | Refs

1.diff (45682B)


      1 diff --git a/README.md b/README.md
      2 index 0d3c137..5dcbdb8 100644
      3 --- a/README.md
      4 +++ b/README.md
      5 @@ -11,18 +11,23 @@ Get and post a screenshot of the Intel Map.
      6  ## Install
      7  To install the plugin you need to:
      8  
      9 -1. Clone this repo into `<yourBotDir>/plugins/`
     10 -2. Optional remove `README.md` `LICENSE` and `.gitignore`
     11 -3. Follow Configuration.
     12 +1. Go into `<yourBotDir>/plugins/`
     13 +2. Clone this repo into `intel_screenbot`
     14 +3. Optional remove `README.md` `LICENSE` and `.gitignore` from `<yourBotDir>/plugins/intel_screenbot`
     15 +4. Run `pip3 install -r requirements.txt`
     16 +5. Follow Configuration.
     17  
     18  ## Configuration
     19  For using the Intel Screenbot you need to add the following to the config.json:
     20  
     21  ```
     22   "intel_screenbot": {
     23 -    "SACSID": "YOUR SACSID",
     24 -    "CSRF": "YOUR CSRF"
     25 -  }
     26 +   "SACSID": "YOUR SACSID",
     27 +   "CSRF": "YOUR CSRF",
     28 +   "plugin_dirs": [
     29 +     "http://iitc.jonatkins.com/release/plugins"
     30 +   ]
     31 + }
     32  ```  
     33  
     34  According to the official INSTALL Documention of the hangoutbot you will find the config.json in `/<username>/.local/share/hangupsbot/`
     35 @@ -30,6 +35,25 @@ According to the official INSTALL Documention of the hangoutbot you will find th
     36  
     37  Also you need to add `intel_screenbot` to the plugins.
     38  
     39 +## How to add gitlab to plugin_dirs
     40 +
     41 +1. Open in browser: `https://gitlab.com/api/v3/projects/search/:REPO_NAME`
     42 +2. copy the number in `id`
     43 +3. add `http://gitlab.com/api/v3/projects/:ID/repository/tree` to `plugin_dirs`
     44 +4. add `"gitlab_token":"YOUR_GITLAB_API_TOKEN"` to `intel_screenbot`
     45 +
     46 +*Note: repos are currently locked to master branch*
     47 +
     48 +## How to add github to plugins_dir
     49 +
     50 +1. add `https://api.github.com/repos/:REPO_USER/:REPO_NAME/git/trees/master?recursive=1`
     51 +
     52 +## How to add local files to plugins_dir
     53 +
     54 +1. just add the absolute path to `plugins_dir` (relative paths are not tested)
     55 +
     56 +*Note: repos are currently locked to master branch*
     57 +
     58  ## How to get SACSID and CSRF
     59  You should look at the Documentation of [ingress-ice](https://github.com/nibogd/ingress-ice/wiki/Cookies-Authentication)
     60  
     61 @@ -41,13 +65,29 @@ You should look at the Documentation of [ingress-ice](https://github.com/nibogd/
     62  `/bot clearintel`  
     63  * Clears the default screenshot URL of a particular hangout.
     64  
     65 +`/bot show_iitcplugins`  
     66 +* Shows every availible IITC-plugin.
     67 +
     68 +`/bot set_iitcplugins <plugin names devided by whitespace>`  
     69 +* Sets the plugins to use with IITC per hangout.
     70 +
     71 +`/bot clear_iitcplugins`  
     72 +* Clear the plugins to use with IITC per hangout.
     73 +
     74 +
     75  ## User Command
     76  
     77 -`/bot intel [<url>]`
     78 +`/bot intel [<url> or <searchTerm>]`
     79  * Provide an arbitrary `<url>` to take a screenshot
     80  * If no `<url>` is supplied, use the default screenshot URL (or reply with an error if no URL is set)
     81  
     82 -## PhantomJS Installation  
     83 +`/bot iitc [<url> or <searchTerm>]`
     84 +* Provide an arbitrary `<url>` to take a screenshot
     85 +* If no `<url>` is supplied, use the default screenshot URL (or reply with an error if no URL is set)
     86 +
     87 +## PhantomJS Installation 
     88 +
     89 +*May be outdated*
     90  
     91  ### Debian-based distros (e.g. Ubuntu 14.04)
     92  
     93 diff --git a/__init__.py b/__init__.py
     94 new file mode 100644
     95 index 0000000..2648a61
     96 --- /dev/null
     97 +++ b/__init__.py
     98 @@ -0,0 +1,315 @@
     99 +from bs4 import BeautifulSoup
    100 +import requests
    101 +import json
    102 +import asyncio, io, logging, os, re, time, tempfile
    103 +import subprocess
    104 +import plugins
    105 +import re
    106 +from asyncio import subprocess
    107 +from shutil import move
    108 +from os import remove, close
    109 +
    110 +logger = logging.getLogger(__name__)
    111 +
    112 +
    113 +def _initialise(bot):
    114 +    plugins.register_user_command(["intel", "iitc"])
    115 +    plugins.register_admin_command(["setintel", "clearintel", "show_iitcplugins", "set_iitcplugins", "clear_iitcplugins"])
    116 +    _get_iitc_plugins(bot)
    117 +
    118 +
    119 +@asyncio.coroutine
    120 +def _open_file(name):
    121 +    logger.debug("opening screenshot file: {}".format(name))
    122 +    return open(name, 'rb')
    123 +
    124 +def _parse_onlineRepos(url, ext=''):
    125 +    logger.debug("parsing github or gitlab or http(s)")
    126 +    page = requests.get(url).text
    127 +    if 'gitlab.com' in url:
    128 +        files = []
    129 +        for json_page in json.loads(page):
    130 +            for attribute, value in json_page.items():
    131 +                if attribute == "name":
    132 +                    if value.endswith(ext):
    133 +                        files.append(url.replace("/tree/", "/blobs/master") + "&filepath="  + value)
    134 +        return files
    135 +    elif 'github.com' in url:
    136 +        files = []
    137 +        for attribute, value in json.loads(page).items():
    138 +            if attribute == "tree":
    139 +                for tree in value:
    140 +                    for attribute, value in tree.items():
    141 +                        if attribute == "path":
    142 +                            if value.endswith(ext):
    143 +                                files.append(url.replace("https://api.github.com/repos/", "https://raw.githubusercontent.com/").replace("git/trees/",'').replace("master?recursive=1","master/") + value)
    144 +        return files
    145 +    else:
    146 +        soup = BeautifulSoup(page, 'html.parser')
    147 +        return [url + '/' + node.get('href') for node in soup.find_all('a') if node.get('href').endswith(ext)]
    148 +
    149 +def _get_iitc_plugins(bot):
    150 +    logger.debug("getting availible plugins")
    151 +    if bot.config.exists(["intel_screenbot", "gitlab_token"]):
    152 +        token = bot.config.get_by_path(["intel_screenbot", "gitlab_token"])
    153 +    url_config = bot.config.get_by_path(["intel_screenbot", "plugin_dirs"])
    154 +    ext = '.user.js'
    155 +    data=[]
    156 +    for url in url_config:
    157 +        if ext in url:
    158 +            item = {"name": url.split('/', url.count('/'))[-1].replace(ext, ''), "url": url}
    159 +            data.append(item)
    160 +        else:
    161 +            if "gitlab.com" in url:
    162 +                url = url + '?private_token=' + token
    163 +            for file in _parse_onlineRepos(url, ext):
    164 +                if "gitlab.com" in url:
    165 +                    item = {"name": file.split('=', file.count('='))[-1].replace(ext, ''), "url": file}
    166 +                elif 'github.com' in url:
    167 +                    item = {"name": file.split('/', file.count('/'))[-1].replace(ext, ''), "url": file}
    168 +                else:
    169 +                    item = {"name": file.split('/', file.count('/'))[-1].replace(ext, ''), "url": file}
    170 +                data.append(item)
    171 +
    172 +    iitc_plugins = data
    173 +    if bot.memory.exists(["iitc_plugins"]):
    174 +        bot.memory.pop_by_path(["iitc_plugins"])
    175 +        bot.memory.set_by_path(["iitc_plugins"], iitc_plugins)
    176 +    else:
    177 +        bot.memory.set_by_path(["iitc_plugins"], iitc_plugins)
    178 +
    179 +@asyncio.coroutine
    180 +def _get_lines(shell_command):
    181 +    p = yield from asyncio.create_subprocess_shell(shell_command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    182 +    stdout, stderr = yield from p.communicate()
    183 +    return p.returncode, stdout
    184 +
    185 +@asyncio.coroutine
    186 +def _screencap(maptype, url, filepath, filename, SACSID, CSRF, plugins, search, bot, event):
    187 +    loop = asyncio.get_event_loop()
    188 +    logger.info("screencapping {} and saving as {}".format(url, filepath))
    189 +    if plugins is '':
    190 +        if search == False:
    191 +            command = 'phantomjs hangupsbot/plugins/intel_screenbot/screencap_' + maptype + '.js "' + SACSID + '" "' + CSRF + '" "' + url + '" "' + filepath + '"'
    192 +            task = _get_lines(command)
    193 +            task = asyncio.wait_for(task, 180.0, loop=self.loop)
    194 +            exitcode, stdout = loop.run_until_complete(task)
    195 +        else:
    196 +            command = 'phantomjs hangupsbot/plugins/intel_screenbot/screencap_' + maptype + '.js "' + SACSID + '" "' + CSRF + '" "' + url + '" "' + filepath + '" "' + search + '"'
    197 +            task = _get_lines(command)
    198 +            task = asyncio.wait_for(task, 180.0, loop=loop)
    199 +            exitcode, stdout = yield from task
    200 +    else:
    201 +        if search == False:
    202 +            command = 'phantomjs hangupsbot/plugins/intel_screenbot/screencap_' + maptype + '.js "' + SACSID + '" "' + CSRF + '" "' + url + '" "' + filepath + '" "' + plugins + '"'
    203 +            task = _get_lines(command)
    204 +            task = asyncio.wait_for(task, 180.0, loop=self.loop)
    205 +            exitcode, stdout = loop.run_until_complete(task)
    206 +        else:
    207 +            command = 'phantomjs hangupsbot/plugins/intel_screenbot/screencap_' + maptype + '.js "' + SACSID + '" "' + CSRF + '" "' + url + '" "' + filepath + '" "' + search + '" "' + plugins + '"'
    208 +            task = _get_lines(command)
    209 +            task = asyncio.wait_for(task, 180.0, loop=loop)
    210 +            exitcode, stdout = yield from task
    211 +
    212 +    # read the resulting file into a byte array
    213 +    file_resource = yield from _open_file(filepath)
    214 +    file_data = yield from loop.run_in_executor(None, file_resource.read)
    215 +    image_data = yield from loop.run_in_executor(None, io.BytesIO, file_data)
    216 +    try:
    217 +        image_id = yield from bot._client.upload_image(image_data, filename=filename)
    218 +        yield from bot._client.sendchatmessage(event.conv.id_, None, image_id=image_id)
    219 +    except Exception as e:
    220 +        yield from bot.coro_send_message(event.conv_id, "<i>error uploading screenshot</i>")
    221 +        logger.exception("upload failed".format(url))
    222 +
    223 +
    224 +def setintel(bot, event, *args):
    225 +    """set url for current converation for the intel or iitc command.
    226 +    use /bot clearintel to clear the previous url before setting a new one.
    227 +    """
    228 +    url = bot.conversation_memory_get(event.conv_id, 'IntelURL')
    229 +    if url is None:
    230 +        bot.conversation_memory_set(event.conv_id, 'IntelURL', ''.join(args))
    231 +        html = "<i><b>{}</b> updated screenshot URL".format(event.user.full_name)
    232 +        yield from bot.coro_send_message(event.conv, html)
    233 +
    234 +    else:
    235 +        html = "<i><b>{}</b> URL already exists for this conversation!<br /><br />".format(event.user.full_name)
    236 +        html += "<i>Clear it first with /bot clearintel before setting a new one."
    237 +        yield from bot.coro_send_message(event.conv, html)
    238 +
    239 +
    240 +def clearintel(bot, event, *args):
    241 +    """clear url for current converation for the intel or iitc command.
    242 +    """
    243 +    url = bot.conversation_memory_get(event.conv_id, 'IntelURL')
    244 +    if url is None:
    245 +        html = "<i><b>{}</b> nothing to clear for this conversation".format(event.user.full_name)
    246 +        yield from bot.coro_send_message(event.conv, html)
    247 +
    248 +    else:
    249 +        bot.conversation_memory_set(event.conv_id, 'IntelURL', None)
    250 +        html = "<i><b>{}</b> URL cleared for this conversation!<br />".format(event.user.full_name)
    251 +        yield from bot.coro_send_message(event.conv, html)
    252 +
    253 +
    254 +def intel(bot, event, *args):
    255 +    """get a screenshot of a search term or intel URL or the default intel URL of the hangout.
    256 +    """
    257 +
    258 +    if args:
    259 +        if len(args) > 1:
    260 +            url = ' '.join(str(i) for i in args)
    261 +        else:
    262 +            url = args[0]
    263 +        if '"' in url:
    264 +            url = url.replace('"', '')
    265 +    else:
    266 +        url = bot.conversation_memory_get(event.conv_id, 'IntelURL')
    267 +
    268 +    if bot.config.exists(["intel_screenbot", "SACSID"]):
    269 +        SACSID = bot.config.get_by_path(["intel_screenbot", "SACSID"])
    270 +    else:
    271 +        html = "<i><b>{}</b> No Intel SACSID Cookie has been added to config. Unable to authenticate".format(event.user.full_name)
    272 +        yield from bot.coro_send_message(event.conv, html)
    273 +    if bot.config.exists(["intel_screenbot", "CSRF"]):
    274 +        CSRF = bot.config.get_by_path(["intel_screenbot", "CSRF"])
    275 +    else:
    276 +        html = "<i><b>{}</b> No Intel CSRF Cookie has been added to config. Unable to authenticate".format(event.user.full_name)
    277 +        yield from bot.coro_send_message(event.conv, html)
    278 +
    279 +    if url is None:
    280 +        html = "<i><b>{}</b> No Intel URL or search term has been set for screenshots.".format(event.user.full_name)
    281 +        yield from bot.coro_send_message(event.conv, html)
    282 +
    283 +    else:
    284 +        if re.match(r'^[a-zA-Z]+://', url):
    285 +            search = False
    286 +            ZoomSearch = re.finditer(r"(?:&z=).*", url)
    287 +            for matchNum, zoomlevel_raw in enumerate(ZoomSearch):
    288 +                matchNum = matchNum + 1
    289 +            zoomlevel_clean = zoomlevel_raw.group()
    290 +            zoomlevel = zoomlevel_clean[3:][:2]
    291 +            if zoomlevel.isdigit():
    292 +                yield from bot.coro_send_message(event.conv_id, "<i>intel map at zoom level "+ zoomlevel + " requested, please wait...</i>")
    293 +            else:
    294 +                yield from bot.coro_send_message(event.conv_id, "<i>intel map at last zoom level requested, please wait...</i>")
    295 +        else:
    296 +            search = url
    297 +            logger.info(search);
    298 +            url = 'https://www.ingress.com/intel'
    299 +            yield from bot.coro_send_message(event.conv_id, "<i>intel map is searching " + search + " and screenshooting as requested, please wait...</i>")
    300 +
    301 +        filename = event.conv_id + "." + str(time.time()) +".png"
    302 +        filepath = tempfile.NamedTemporaryFile(prefix=event.conv_id, suffix=".png", delete=False).name
    303 +        logger.debug("temporary screenshot file: {}".format(filepath))
    304 +        try:
    305 +            loop = asyncio.get_event_loop()
    306 +            image_data = yield from _screencap("intel", url, filepath, filename, SACSID, CSRF, "", search, bot, event)
    307 +        except Exception as e:
    308 +            yield from bot.coro_send_message(event.conv_id, "<i>error getting screenshot</i>")
    309 +            logger.exception("screencap failed".format(url))
    310 +            return
    311 +
    312 +
    313 +def iitc(bot, event, *args):
    314 +    """get a screenshot of a search term or intel URL or the default intel URL of the hangout.
    315 +    """
    316 +
    317 +    if args:
    318 +        if len(args) > 1:
    319 +            url = ' '.join(str(i) for i in args)
    320 +        else:
    321 +            url = args[0]
    322 +        if '"' in url:
    323 +            url = url.replace('"', '')
    324 +    else:
    325 +        url = bot.conversation_memory_get(event.conv_id, 'IntelURL')
    326 +
    327 +    if bot.config.exists(["intel_screenbot", "SACSID"]):
    328 +        SACSID = bot.config.get_by_path(["intel_screenbot", "SACSID"])
    329 +    else:
    330 +        html = "<i><b>{}</b> No Intel SACSID Cookie has been added to config. Unable to authenticate".format(event.user.full_name)
    331 +        yield from bot.coro_send_message(event.conv, html)
    332 +    if bot.config.exists(["intel_screenbot", "CSRF"]):
    333 +        CSRF = bot.config.get_by_path(["intel_screenbot", "CSRF"])
    334 +    else:
    335 +        html = "<i><b>{}</b> No Intel CSRF Cookie has been added to config. Unable to authenticate".format(event.user.full_name)
    336 +        yield from bot.coro_send_message(event.conv, html)
    337 +
    338 +    if url is None:
    339 +        html = "<i><b>{}</b> No Intel URL has been set for screenshots.".format(event.user.full_name)
    340 +        yield from bot.coro_send_message(event.conv, html)
    341 +
    342 +    else:
    343 +        if re.match(r'^[a-zA-Z]+://', url):
    344 +            search = False
    345 +            ZoomSearch = re.finditer(r"(?:&z=).*", url)
    346 +            for matchNum, zoomlevel_raw in enumerate(ZoomSearch):
    347 +                matchNum = matchNum + 1
    348 +            zoomlevel_clean = zoomlevel_raw.group()
    349 +            zoomlevel = zoomlevel_clean[3:][:2]
    350 +            if zoomlevel.isdigit():
    351 +                yield from bot.coro_send_message(event.conv_id, "<i>intel map at zoom level "+ zoomlevel + " requested, please wait...</i>")
    352 +            else:
    353 +                yield from bot.coro_send_message(event.conv_id, "<i>intel map at last zoom level requested, please wait...</i>")
    354 +        else:
    355 +            search = url
    356 +            logger.info(search);
    357 +            url = 'https://www.ingress.com/intel'
    358 +            yield from bot.coro_send_message(event.conv_id, "<i>intel map is searching " + search + " and screenshooting as requested, please wait...</i>")
    359 +
    360 +        filename = event.conv_id + "." + str(time.time()) +".png"
    361 +        filepath = tempfile.NamedTemporaryFile(prefix=event.conv_id, suffix=".png", delete=False).name
    362 +        plugins_filepath = tempfile.NamedTemporaryFile(prefix=event.conv_id, suffix=".json", delete=False).name
    363 +        logger.debug("temporary screenshot file: {}".format(filepath))
    364 +        if bot.conversation_memory_get(event.conv_id, 'iitc_plugins'):
    365 +            plugins = []
    366 +            plugin_names = bot.conversation_memory_get(event.conv_id, 'iitc_plugins').split(", ")
    367 +            if bot.memory.exists(["iitc_plugins"]):
    368 +                for plugin_objects in bot.memory.get_by_path(["iitc_plugins"]):
    369 +                    for plugin_name in plugin_names:
    370 +                        if plugin_objects["name"]  == plugin_name:
    371 +                            plugins.append(plugin_objects["url"])
    372 +        else:
    373 +             plugins = ''
    374 +        
    375 +        with open(plugins_filepath, 'w') as out:
    376 +            out.write(json.dumps(plugins))
    377 +        
    378 +        try:
    379 +            loop = asyncio.get_event_loop()
    380 +            image_data = yield from _screencap("iitc", url, filepath, filename, SACSID, CSRF, plugins_filepath, search, bot, event)
    381 +        except Exception as e:
    382 +            yield from bot.coro_send_message(event.conv_id, "<i>error getting screenshot</i>")
    383 +            logger.exception("screencap failed".format(url))
    384 +            return
    385 +
    386 +def show_iitcplugins(bot, event, *args):
    387 +    if bot.memory.exists(["iitc_plugins"]):
    388 +        plugin_names = []
    389 +        for plugin_objects in bot.memory.get_by_path(["iitc_plugins"]):
    390 +            for attribute, value in plugin_objects.items():
    391 +                if attribute == "name":
    392 +                    plugin_names.append(value)
    393 +        yield from bot.coro_send_to_user_and_conversation(event.user.id_.chat_id, event.conv_id, "<i><b>IITC Plugins:</b><br> {}</i>".format(', <br>'.join(str(i) for i in plugin_names)), _("<i><b>{}</b>, I've sent you the plugins ;)</i>").format(event.user.full_name))
    394 +
    395 +def set_iitcplugins(bot, event, *args):
    396 +    if not bot.conversation_memory_get(event.conv_id, 'iitc_plugins') is None:
    397 +        html = "<i><b>{}</b> plugins already set for this conversation!<br /><br />".format(event.user.full_name)
    398 +        html += "<i>Clear them first with /bot clear_iitcplugins before setting new ones."
    399 +        yield from bot.coro_send_message(event.conv, html)
    400 +    else:
    401 +        bot.conversation_memory_set(event.conv_id, 'iitc_plugins', ', '.join(args))
    402 +        html = "<i><b>{}</b> updated plugins".format(event.user.full_name)
    403 +        yield from bot.coro_send_message(event.conv, html)
    404 +
    405 +def clear_iitcplugins(bot, event, *args):
    406 +    if bot.conversation_memory_get(event.conv_id, 'iitc_plugins') is None:
    407 +        html = "<i><b>{}</b> nothing to clear for this conversation".format(event.user.full_name)
    408 +        yield from bot.coro_send_message(event.conv, html)
    409 +
    410 +    else:
    411 +        bot.conversation_memory_set(event.conv_id, 'iitc_plugins', None)
    412 +        html = "<i><b>{}</b> plugins cleared for this conversation!<br />".format(event.user.full_name)
    413 +        yield from bot.coro_send_message(event.conv, html)
    414 diff --git a/intel_screenbot/__init__.py b/intel_screenbot/__init__.py
    415 deleted file mode 100644
    416 index 880436e..0000000
    417 --- a/intel_screenbot/__init__.py
    418 +++ /dev/null
    419 @@ -1,136 +0,0 @@
    420 -import asyncio, io, logging, os, re, time, tempfile
    421 -import subprocess
    422 -import plugins
    423 -import re
    424 -from asyncio import subprocess
    425 -
    426 -logger = logging.getLogger(__name__)
    427 -
    428 -
    429 -def _initialise(bot):
    430 -    plugins.register_user_command(["intel"])
    431 -    plugins.register_admin_command(["setintel", "clearintel"])
    432 -    
    433 -@asyncio.coroutine
    434 -def _open_file(name):
    435 -    logger.debug("opening screenshot file: {}".format(name))
    436 -    return open(name, 'rb')
    437 -
    438 -@asyncio.coroutine
    439 -def _get_lines(shell_command):
    440 -    p = yield from asyncio.create_subprocess_shell(shell_command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    441 -    stdout, stderr = yield from p.communicate()
    442 -    return p.returncode, stdout
    443 -
    444 -@asyncio.coroutine
    445 -def _screencap(url, filepath, filename, SACSID, CSRF, search, bot, event):
    446 -    loop = asyncio.get_event_loop()
    447 -    logger.info("screencapping {} and saving as {}".format(url, filepath))
    448 -    if search == False:
    449 -        command = 'phantomjs hangupsbot/plugins/intel_screenbot/screencap.js "' + SACSID + '" "' + CSRF + '" "' + url + '" "' + filepath + '"'
    450 -        task = _get_lines(command)
    451 -        task = asyncio.wait_for(task, 180.0, loop=self.loop)
    452 -        exitcode, stdout = loop.run_until_complete(task)
    453 -    else:
    454 -        command = 'phantomjs hangupsbot/plugins/intel_screenbot/screencap.js "' + SACSID + '" "' + CSRF + '" "' + url + '" "' + filepath + '" "' + search + '"'
    455 -        task = _get_lines(command)
    456 -        task = asyncio.wait_for(task, 180.0, loop=loop)
    457 -        exitcode, stdout = yield from task
    458 -
    459 -    # read the resulting file into a byte array
    460 -    file_resource = yield from _open_file(filepath)
    461 -    file_data = yield from loop.run_in_executor(None, file_resource.read)
    462 -    image_data = yield from loop.run_in_executor(None, io.BytesIO, file_data)
    463 -    try:
    464 -        image_id = yield from bot._client.upload_image(image_data, filename=filename)
    465 -        yield from bot._client.sendchatmessage(event.conv.id_, None, image_id=image_id)
    466 -    except Exception as e:
    467 -        yield from bot.coro_send_message(event.conv_id, "<i>error uploading screenshot</i>")
    468 -        logger.exception("upload failed".format(url))
    469 -
    470 -
    471 -def setintel(bot, event, *args):
    472 -    """set url for current converation for the screenshot command. 
    473 -    use /bot clearintel to clear the previous url before setting a new one.
    474 -    """
    475 -    url = bot.conversation_memory_get(event.conv_id, 'IntelURL')
    476 -    if url is None:
    477 -        bot.conversation_memory_set(event.conv_id, 'IntelURL', ''.join(args))
    478 -        html = "<i><b>{}</b> updated screenshot URL".format(event.user.full_name)
    479 -        yield from bot.coro_send_message(event.conv, html)
    480 -
    481 -    else:
    482 -        html = "<i><b>{}</b> URL already exists for this conversation!<br /><br />".format(event.user.full_name)
    483 -        html += "<i>Clear it first with /bot clearintel before setting a new one."
    484 -        yield from bot.coro_send_message(event.conv, html)
    485 -
    486 -
    487 -def clearintel(bot, event, *args):
    488 -    """clear url for current converation for the screenshot command. 
    489 -    """
    490 -    url = bot.conversation_memory_get(event.conv_id, 'IntelURL')
    491 -    if url is None:
    492 -        html = "<i><b>{}</b> nothing to clear for this conversation".format(event.user.full_name)
    493 -        yield from bot.coro_send_message(event.conv, html)
    494 -
    495 -    else:
    496 -        bot.conversation_memory_set(event.conv_id, 'IntelURL', None)
    497 -        html = "<i><b>{}</b> URL cleared for this conversation!<br />".format(event.user.full_name)
    498 -        yield from bot.coro_send_message(event.conv, html)
    499 -
    500 -
    501 -def intel(bot, event, *args):
    502 -    """get a screenshot of a user provided URL or the default URL of the hangout. 
    503 -    """
    504 -                                    
    505 -    if args:
    506 -        if len(args) > 1:
    507 -            url = ' '.join(str(i) for i in args)
    508 -        else:
    509 -            url = args[0]
    510 -    else:
    511 -        url = bot.conversation_memory_get(event.conv_id, 'IntelURL')
    512 -                                    
    513 -    if bot.config.exists(["intel_screenbot", "SACSID"]):
    514 -        SACSID = bot.config.get_by_path(["intel_screenbot", "SACSID"])
    515 -    else:
    516 -        html = "<i><b>{}</b> No Intel SACSID Cookie has been added to config. Unable to authenticate".format(event.user.full_name)
    517 -        yield from bot.coro_send_message(event.conv, html)
    518 -    if bot.config.exists(["intel_screenbot", "CSRF"]):
    519 -        CSRF = bot.config.get_by_path(["intel_screenbot", "CSRF"])
    520 -    else:
    521 -        html = "<i><b>{}</b> No Intel CSRF Cookie has been added to config. Unable to authenticate".format(event.user.full_name)
    522 -        yield from bot.coro_send_message(event.conv, html)
    523 -        
    524 -    if url is None:
    525 -        html = "<i><b>{}</b> No Intel URL has been set for screenshots.".format(event.user.full_name)
    526 -        yield from bot.coro_send_message(event.conv, html)
    527 -                                    
    528 -    else:        
    529 -        if re.match(r'^[a-zA-Z]+://', url):
    530 -            search = False
    531 -            ZoomSearch = re.finditer(r"(?:&z=).*", url)
    532 -            for matchNum, zoomlevel_raw in enumerate(ZoomSearch):
    533 -                matchNum = matchNum + 1
    534 -            zoomlevel_clean = zoomlevel_raw.group()
    535 -            zoomlevel = zoomlevel_clean[3:][:2]
    536 -            if zoomlevel.isdigit():
    537 -                yield from bot.coro_send_message(event.conv_id, "<i>intel map at zoom level "+ zoomlevel + " requested, please wait...</i>")
    538 -            else:
    539 -                yield from bot.coro_send_message(event.conv_id, "<i>intel map at last zoom level requested, please wait...</i>")
    540 -        else:
    541 -            search = url
    542 -            logger.info(search);
    543 -            url = 'https://www.ingress.com/intel'
    544 -            yield from bot.coro_send_message(event.conv_id, "<i>intel map is searching " + search + " and screenshooting as requested, please wait...</i>")
    545 -        filename = event.conv_id + "." + str(time.time()) +".png"
    546 -        filepath = tempfile.NamedTemporaryFile(prefix=event.conv_id, suffix=".png", delete=False).name
    547 -        logger.debug("temporary screenshot file: {}".format(filepath))
    548 -
    549 -        try:
    550 -            loop = asyncio.get_event_loop()
    551 -            image_data = yield from _screencap(url, filepath, filename, SACSID, CSRF, search, bot, event)
    552 -        except Exception as e:
    553 -            yield from bot.coro_send_message(event.conv_id, "<i>error getting screenshot</i>")
    554 -            logger.exception("screencap failed".format(url))
    555 -            return
    556 diff --git a/requirements.txt b/requirements.txt
    557 new file mode 100644
    558 index 0000000..9d981c3
    559 --- /dev/null
    560 +++ b/requirements.txt
    561 @@ -0,0 +1,2 @@
    562 +bs4
    563 +requests
    564 diff --git a/screencap_iitc.js b/screencap_iitc.js
    565 new file mode 100644
    566 index 0000000..c9413db
    567 --- /dev/null
    568 +++ b/screencap_iitc.js
    569 @@ -0,0 +1,278 @@
    570 +var system = require('system');
    571 +var args = system.args;
    572 +var page = require('webpage').create();
    573 +var fs = require('fs');
    574 +if (args.length === 1) {
    575 +    console.log('Try to pass some args when invoking this script!');
    576 +} else {
    577 +  if (args.length === 6){
    578 +      var SACSID  = args[1];
    579 +      var CSRF  = args[2];
    580 +      var IntelURL  = args[3];
    581 +      var filepath  = args[4];
    582 +      var plugins_file  = args[5];
    583 +      var search  = 'nix';
    584 +  }else{
    585 +    if (args.length === 7){
    586 +      var SACSID  = args[1];
    587 +      var CSRF  = args[2];
    588 +      var IntelURL  = args[3];
    589 +      var filepath  = args[4];
    590 +      var search  = args[5];
    591 +      var plugins_file  = args[6];
    592 +    }
    593 +  }
    594 +}
    595 +
    596 +function addCookies(sacsid, csrf) {
    597 +  phantom.addCookie({
    598 +    name: 'SACSID',
    599 +    value: sacsid,
    600 +    domain: 'www.ingress.com',
    601 +    path: '/',
    602 +    httponly: true,
    603 +    secure: true
    604 +  });
    605 +  phantom.addCookie({
    606 +    name: 'csrftoken',
    607 +    value: csrf,
    608 +    domain: 'www.ingress.com',
    609 +    path: '/'
    610 +  });
    611 +}
    612 +
    613 +
    614 +function waitFor ($config) {
    615 +    $config._start = $config._start || new Date();
    616 +    if ($config.timeout && new Date - $config._start > $config.timeout) {
    617 +        if ($config.error) $config.error();
    618 +        if ($config.debug) console.log('timedout ' + (new Date - $config._start) + 'ms');
    619 +        return;
    620 +    }
    621 +    if ($config.check()) {
    622 +        if ($config.debug) console.log('success ' + (new Date - $config._start) + 'ms');
    623 +        return $config.success();
    624 +    }
    625 +    setTimeout(waitFor, $config.interval || 0, $config);
    626 +}
    627 +
    628 +function loadIitcPlugin(src) {
    629 +  page.evaluate(function(src) {
    630 +    var script = document.createElement('script');
    631 +    script.type='text/javascript';
    632 +    script.src=src;
    633 +    document.head.insertBefore(script, document.head.lastChild);
    634 +  }, src);
    635 +}
    636 +
    637 +function loadLocalIitcPlugin(src) {
    638 +    page.injectJs(src)
    639 +}
    640 +
    641 +addCookies(SACSID, CSRF);
    642 +afterCookieLogin(IntelURL, search);
    643 +
    644 +function afterCookieLogin(IntelURL, search) {
    645 +  page.viewportSize = { width: '1920', height: '1080' };
    646 +  page.open(IntelURL, function(status) {
    647 +    if (status !== 'success') {quit('unable to connect to remote server')}
    648 +    page.injectJs('https://code.jquery.com/jquery-3.1.1.min.js');
    649 +    setTimeout(function() {
    650 +        page.evaluate(function() {
    651 +            localStorage['ingress.intelmap.layergroupdisplayed'] = JSON.stringify({
    652 +              "Unclaimed Portals":Boolean(1 === 1),
    653 +              "Level 1 Portals":Boolean(1 === 1),
    654 +              "Level 2 Portals":Boolean((1 <= 2) && (8 >= 2)),
    655 +              "Level 3 Portals":Boolean((1 <= 3) && (8 >= 3)),
    656 +              "Level 4 Portals":Boolean((1 <= 4) && (8 >= 4)),
    657 +              "Level 5 Portals":Boolean((1 <= 5) && (8 >= 5)),
    658 +              "Level 6 Portals":Boolean((1 <= 6) && (8 >= 6)),
    659 +              "Level 7 Portals":Boolean((1 <= 7) && (8 >= 7)),
    660 +              "Level 8 Portals":Boolean(8 === 8),
    661 +              "DEBUG Data Tiles":false,
    662 +              "Artifacts":true,
    663 +              "Ornaments":true
    664 +            });
    665 +            var script = document.createElement('script');
    666 +            script.type='text/javascript';
    667 +            script.src='https://secure.jonatkins.com/iitc/release/total-conversion-build.user.js';
    668 +            document.head.insertBefore(script, document.head.lastChild);
    669 +        });
    670 +        loadIitcPlugin('http://iitc.jonatkins.com/release/plugins/canvas-render.user.js');
    671 +        var plugins = JSON.parse(fs.read(plugins_file));
    672 +        for(var i in plugins){
    673 +            var plugin = plugins[i];
    674 +            if(plugin.match('^[a-zA-Z]+://')){
    675 +                loadIitcPlugin(plugin);
    676 +            }else{
    677 +               loadLocalIitcPlugin(plugin);
    678 +            }
    679 +        }
    680 +        setTimeout(function() {
    681 +            if (search != "nix") {
    682 +                page.evaluate(function(search) {
    683 +                    if (document.querySelector('#search')){
    684 +                      window.setTimeout(function() {
    685 +                        document.getElementById("search").value=search;
    686 +                        var e = jQuery.Event("keypress");
    687 +                        e.which = 13;
    688 +                        e.keyCode = 13;
    689 +                        $("#search").trigger(e);
    690 +                      }, 2000);
    691 +                      var checkExist = setInterval(function() {
    692 +                        if ($('.searchquery').length > 0) {
    693 +                            window.setTimeout(function() {$('.searchquery > :nth-child(2)').children()[0].click();}, 1000);
    694 +                            clearInterval(checkExist);
    695 +                        }
    696 +                      }, 100);             
    697 +                    }
    698 +                }, search);
    699 +            }
    700 +            waitFor({
    701 +                timeout: 120000,
    702 +                check: function () {
    703 +                    return page.evaluate(function() {
    704 +                        if (document.querySelector('.map').textContent.indexOf('done') != -1) {
    705 +                            return true;
    706 +                        }else{
    707 +                            return false;
    708 +                        }
    709 +                    });
    710 +                },
    711 +                success: function () {
    712 +                    hideDebris();
    713 +                    prepare('1920', '1080');
    714 +                    main();
    715 +                },
    716 +                error: function () {
    717 +                    hideDebris();
    718 +                    prepare('1920', '1080');
    719 +                    main();
    720 +                }
    721 +            });
    722 +        }, "5000");
    723 +    }, "5000");
    724 +  });
    725 +}
    726 +
    727 +function s(file) {
    728 +  page.render(file);
    729 +  phantom.exit(0);
    730 +}
    731 +
    732 +function hideDebris() {
    733 +  window.setTimeout(function() {
    734 +    page.evaluate(function() {
    735 +      if (document.querySelector('#chat'))                      {document.querySelector('#chat').style.display = 'none';}
    736 +      if (document.querySelector('#chatcontrols'))              {document.querySelector('#chatcontrols').style.display = 'none';}
    737 +      if (document.querySelector('#chatinput'))                 {document.querySelector('#chatinput').style.display = 'none';}
    738 +      if (document.querySelector('#updatestatus'))              {document.querySelector('#updatestatus').style.display = 'none';}
    739 +      if (document.querySelector('#sidebartoggle'))             {document.querySelector('#sidebartoggle').style.display = 'none';}
    740 +      if (document.querySelector('#scrollwrapper'))             {document.querySelector('#scrollwrapper').style.display = 'none';}
    741 +      if (document.querySelector('.leaflet-control-container')) {document.querySelector('.leaflet-control-container').style.display = 'none';}
    742 +    });
    743 +  }, 2000);
    744 +}
    745 +
    746 +function prepare(widthz, heightz) {
    747 +        window.setTimeout(function() {
    748 +          page.evaluate(function(w, h) {
    749 +            $("span:contains(' Google Roads')").prev().click();
    750 +            var water = document.createElement('p');
    751 +            water.id='viewport-ice';
    752 +            water.style.position = 'absolute';
    753 +            water.style.top = '0';
    754 +            water.style.marginTop = '0';
    755 +            water.style.paddingTop = '0';
    756 +            water.style.left = '0';
    757 +            water.style.width = w + 'px';
    758 +            water.style.height = h + 'px';
    759 +            document.querySelectorAll('body')[0].appendChild(water);
    760 +          }, widthz, heightz);
    761 +          var selector = "#viewport-ice";
    762 +          setElementBounds(selector);
    763 +        }, 4000);
    764 +}
    765 +
    766 +
    767 +function setElementBounds(selector) {
    768 +  page.clipRect = page.evaluate(function(selector) {
    769 +    var clipRect = document.querySelector(selector).getBoundingClientRect();
    770 +    return {
    771 +      top:    clipRect.top,
    772 +      left:   clipRect.left,
    773 +      width:  clipRect.width,
    774 +      height: clipRect.height
    775 +    };
    776 +  }, selector);
    777 +}
    778 +
    779 +function getDateTime(format) {
    780 +  var now     = new Date();
    781 +  var year    = now.getFullYear();
    782 +  var month   = now.getMonth()+1;
    783 +  var day     = now.getDate();
    784 +  var hour    = now.getHours();
    785 +  var minute  = now.getMinutes();
    786 +  var second  = now.getSeconds();
    787 +  var timeZone = '';
    788 +  if(month.toString().length === 1) {
    789 +    month = '0' + month;
    790 +  }
    791 +  if(day.toString().length === 1) {
    792 +    day = '0' + day;
    793 +  }
    794 +  if(hour.toString().length === 1) {
    795 +    hour = '0' + hour;
    796 +  }
    797 +  if(minute.toString().length === 1) {
    798 +    minute = '0' + minute;
    799 +  }
    800 +  if(second.toString().length === 1) {
    801 +    second = '0' + second;
    802 +  }
    803 +  var dateTime;
    804 +  if (format === 1) {
    805 +    dateTime = year + '-' + month + '-' + day + '--' + hour + '-' + minute + '-' + second;
    806 +  } else {
    807 +    dateTime = day + '.' + month + '.' + year + ' ' + hour + ':' + minute + ':' + second + timeZone;
    808 +  }
    809 +  return dateTime;
    810 +}
    811 +
    812 +function addTimestamp(time) {
    813 +  page.evaluate(function(dateTime) {
    814 +    var water = document.createElement('p');
    815 +    water.id='watermark-ice';
    816 +    water.innerHTML = dateTime;
    817 +    water.style.position = 'absolute';
    818 +    water.style.color = '#3A539B';
    819 +    water.style.top = '0';
    820 +    water.style.zIndex = '4404';
    821 +    water.style.marginTop = '0';
    822 +    water.style.paddingTop = '0';
    823 +    water.style.left = '0';
    824 +    water.style.fontSize = '40px';
    825 +    water.style.opacity = '0.8';
    826 +    water.style.fontFamily = 'monospace';
    827 +    water.style.textShadow = 'rgb(3, 3, 3) 7px 5px 9px';
    828 +    document.querySelectorAll('body')[0].appendChild(water);
    829 +  }, time);
    830 +}
    831 +
    832 +/**
    833 + * Main function.
    834 + */
    835 +function main() {
    836 +  page.evaluate(function() {
    837 +    if (document.getElementById('watermark-ice')) {
    838 +      var oldStamp = document.getElementById('watermark-ice');
    839 +      oldStamp.parentNode.removeChild(oldStamp);
    840 +    }
    841 +  });
    842 +  window.setTimeout(function() {
    843 +    addTimestamp(getDateTime(0));
    844 +    file = filepath;
    845 +    s(file);
    846 +  }, 3000);
    847 +}
    848 diff --git a/intel_screenbot/screencap.js b/screencap_intel.js
    849 similarity index 55%
    850 rename from intel_screenbot/screencap.js
    851 rename to screencap_intel.js
    852 index 9383f15..3ef8afc 100644
    853 --- a/intel_screenbot/screencap.js
    854 +++ b/screencap_intel.js
    855 @@ -1,5 +1,5 @@
    856  var system = require('system')
    857 -var args = require('system').args;
    858 +var args = system.args;
    859  var page = require('webpage').create();
    860  var fs = require('fs');
    861  if (args.length === 1) {
    862 @@ -18,31 +18,12 @@ if (args.length === 1) {
    863        var IntelURL  = args[3];
    864        var filepath  = args[4];
    865        var search  = args[5];
    866 -      console.log(search)
    867 -      system.stdout.writeLine(filepath);
    868      }
    869    }
    870  }
    871  
    872 -addCookies(SACSID,CSRF)
    873 -afterCookieLogin(IntelURL, search)
    874 -
    875 -function waitFor ($config) {
    876 -    $config._start = $config._start || new Date();
    877 -
    878 -    if ($config.timeout && new Date - $config._start > $config.timeout) {
    879 -        if ($config.error) $config.error();
    880 -        if ($config.debug) console.log('timedout ' + (new Date - $config._start) + 'ms');
    881 -        return;
    882 -    }
    883 -
    884 -    if ($config.check()) {
    885 -        if ($config.debug) console.log('success ' + (new Date - $config._start) + 'ms');
    886 -        return $config.success();
    887 -    }
    888 -
    889 -    setTimeout(waitFor, $config.interval || 0, $config);
    890 -}
    891 +addCookies(SACSID,CSRF);
    892 +afterCookieLogin(IntelURL, search);
    893  
    894  function addCookies(sacsid, csrf) {
    895    phantom.addCookie({
    896 @@ -61,26 +42,30 @@ function addCookies(sacsid, csrf) {
    897    });
    898  }
    899  
    900 +function waitFor ($config) {
    901 +    $config._start = $config._start || new Date();
    902 +    if ($config.timeout && new Date - $config._start > $config.timeout) {
    903 +        if ($config.error) $config.error();
    904 +        if ($config.debug) console.log('timedout ' + (new Date - $config._start) + 'ms');
    905 +        return;
    906 +    }
    907 +    if ($config.check()) {
    908 +        if ($config.debug) console.log('success ' + (new Date - $config._start) + 'ms');
    909 +        return $config.success();
    910 +    }
    911 +    setTimeout(waitFor, $config.interval || 0, $config);
    912 +}
    913  
    914 -/**
    915 - * Does all stuff needed after cookie authentication
    916 - * @since 3.1.0
    917 - */
    918  function afterCookieLogin(IntelURL, search) {
    919    page.open(IntelURL, function(status) {
    920      if (status !== 'success') {quit('unable to connect to remote server')}
    921  
    922 -    if(!isSignedIn()) {
    923 -      if(fs.exists('.iced_cookies')) {
    924 -        fs.remove('.iced_cookies');
    925 -      }
    926 -    }
    927      setTimeout(function() {
    928          waitFor({
    929              timeout: 120000,
    930              check: function () {
    931                  return page.evaluate(function() {
    932 -                    if (document.querySelector('#percent_text').textContent == "90") {
    933 +                    if (document.querySelector('#percent_text').textContent.indexOf('90') != -1) {
    934                          if (!document.getElementById("loading_msg").style.display){
    935                              return true;
    936                          }else{
    937 @@ -100,91 +85,58 @@ function afterCookieLogin(IntelURL, search) {
    938                  main();
    939              },
    940              error: function () {
    941 -                system.stdout.writeLine('map did not finish loading in time...');
    942                  page.evaluate(function() {
    943                      document.querySelector("#filters_container").style.display= 'none';
    944                  });
    945                  hideDebris();
    946                  prepare('1920', '1080', search);
    947                  main();
    948 -            } // optional
    949 +            }
    950          });
    951      }, "5000");
    952    });
    953  }
    954  
    955 -/**
    956 - * Checks if user is signed in by looking for the "Sign in" button
    957 - * @returns {boolean}
    958 - * @since 3.2.0
    959 - */
    960 -function isSignedIn() {
    961 -  return page.evaluate(function() {
    962 -    return document.getElementsByTagName('a')[0].innerText.trim() !== 'Sign in';
    963 -  });
    964 -}
    965 -
    966 -function storeCookies() {
    967 -  var cookies = page.cookies;
    968 -  fs.write('.iced_cookies', '', 'w');
    969 -  for(var i in cookies) {
    970 -    fs.write('.iced_cookies', cookies[i].name + '=' + cookies[i].value +'\n', 'a');
    971 -  }
    972 -}
    973 -
    974  function s(file) {
    975    page.render(file);
    976    phantom.exit(0);
    977  }
    978  
    979  function hideDebris() {
    980 -    system.stdout.writeLine('hideDebris...');
    981 -    page.evaluate(function() {
    982 -      if (document.querySelector('#comm'))             {document.querySelector('#comm').style.display = 'none';}
    983 -      if (document.querySelector('#player_stats'))     {document.querySelector('#player_stats').style.display = 'none';}
    984 -      if (document.querySelector('#game_stats'))       {document.querySelector('#game_stats').style.display = 'none';}
    985 -      if (document.querySelector('#geotools'))         {document.querySelector('#geotools').style.display = 'none';}
    986 -      if (document.querySelector('#header'))           {document.querySelector('#header').style.display = 'none';}
    987 -      if (document.querySelector('#snapcontrol'))      {document.querySelector('#snapcontrol').style.display = 'none';}
    988 -      if (document.querySelectorAll('.img_snap')[0])   {document.querySelectorAll('.img_snap')[0].style.display = 'none';}
    989 -      if (document.querySelector('#display_msg_text')) {document.querySelector('#display_msg_text').style.display = 'none';}
    990 -    });
    991 -    page.evaluate(function() {
    992 -      var hide = document.querySelectorAll('.gmnoprint');
    993 -      for (var index = 0; index < hide.length; ++index) {
    994 -        hide[index].style.display = 'none';
    995 -      }
    996 -    });
    997 +  page.evaluate(function() {
    998 +    if (document.querySelector('#comm'))             {document.querySelector('#comm').style.display = 'none';}
    999 +    if (document.querySelector('#player_stats'))     {document.querySelector('#player_stats').style.display = 'none';}
   1000 +    if (document.querySelector('#game_stats'))       {document.querySelector('#game_stats').style.display = 'none';}
   1001 +    if (document.querySelector('#geotools'))         {document.querySelector('#geotools').style.display = 'none';}
   1002 +    if (document.querySelector('#header'))           {document.querySelector('#header').style.display = 'none';}
   1003 +    if (document.querySelector('#snapcontrol'))      {document.querySelector('#snapcontrol').style.display = 'none';}
   1004 +    if (document.querySelectorAll('.img_snap')[0])   {document.querySelectorAll('.img_snap')[0].style.display = 'none';}
   1005 +    if (document.querySelector('#display_msg_text')) {document.querySelector('#display_msg_text').style.display = 'none';}
   1006 +  });
   1007 +  page.evaluate(function() {
   1008 +    var hide = document.querySelectorAll('.gmnoprint');
   1009 +    for (var index = 0; index < hide.length; ++index) {
   1010 +      hide[index].style.display = 'none';
   1011 +    }
   1012 +  });
   1013  }
   1014  
   1015 -/**
   1016 - * Prepare map for screenshooting. Make screenshots same width and height with map_canvas
   1017 - * If IITC, also set width and height
   1018 - * @param {boolean} iitcz
   1019 - * @param {number} widthz
   1020 - * @param {number} heightz
   1021 - */
   1022  function prepare(widthz, heightz, search) {
   1023 -    system.stdout.writeLine('prepare...');
   1024 -    if (search == "nix") {
   1025 -        var selector = "#map_canvas";
   1026 -        setElementBounds(selector);
   1027 -    }else{
   1028 -        page.evaluate(function(search) {
   1029 -            if (document.querySelector('#geocode')){
   1030 -                document.getElementById("address").value=search;
   1031 -                document.querySelector("input[value=Search]").click();
   1032 -            }
   1033 -        }, search);
   1034 -        var selector = "#map_canvas";
   1035 -        setElementBounds(selector);
   1036 -    }
   1037 +  if (search == "nix") {
   1038 +    var selector = "#map_canvas";
   1039 +    setElementBounds(selector);
   1040 +  }else{
   1041 +    page.evaluate(function(search) {
   1042 +      if (document.querySelector('#geocode')){
   1043 +        document.getElementById("address").value=search;
   1044 +        document.querySelector("input[value=Search]").click();
   1045 +      }
   1046 +    }, search);
   1047 +    var selector = "#map_canvas";
   1048 +    setElementBounds(selector);
   1049 +  }
   1050  }
   1051  
   1052 -/**
   1053 - * Sets element bounds
   1054 - * @param selector
   1055 - */
   1056  function setElementBounds(selector) {
   1057    page.clipRect = page.evaluate(function(selector) {
   1058      var clipRect = document.querySelector(selector).getBoundingClientRect();
   1059 @@ -197,10 +149,6 @@ function setElementBounds(selector) {
   1060    }, selector);
   1061  }
   1062  
   1063 -/**
   1064 - * Checks if human presence not detected and makes a human present
   1065 - * @since 2.3.0
   1066 - */
   1067  function humanPresence() {
   1068    var outside = page.evaluate(function() {
   1069      return !!(document.getElementById('butterbar') && (document.getElementById('butterbar').style.display !== 'none'));
   1070 @@ -247,37 +195,34 @@ function getDateTime(format) {
   1071  }
   1072  
   1073  function addTimestamp(time) {
   1074 -    page.evaluate(function(dateTime) {
   1075 -      var water = document.createElement('p');
   1076 -      water.id='watermark-ice';
   1077 -      water.innerHTML = dateTime;
   1078 -      water.style.position = 'absolute';
   1079 -      water.style.color = 'orange';
   1080 -      water.style.top = '0';
   1081 -      water.style.left = '0';
   1082 -      water.style.fontSize = '40px';
   1083 -      water.style.opacity = '0.8';
   1084 -      water.style.marginTop = '0';
   1085 -      water.style.paddingTop = '0';
   1086 -      water.style.fontFamily = 'monospace';
   1087 -      water.style.textShadow = '2px 2px 5px #111717';
   1088 -      document.querySelector('#map_canvas').appendChild(water);
   1089 -    }, time);
   1090 +  page.evaluate(function(dateTime) {
   1091 +    var water = document.createElement('p');
   1092 +    water.id='watermark-ice';
   1093 +    water.innerHTML = dateTime;
   1094 +    water.style.position = 'absolute';
   1095 +    water.style.color = 'orange';
   1096 +    water.style.top = '0';
   1097 +    water.style.left = '0';
   1098 +    water.style.fontSize = '40px';
   1099 +    water.style.opacity = '0.8';
   1100 +    water.style.marginTop = '0';
   1101 +    water.style.paddingTop = '0';
   1102 +    water.style.fontFamily = 'monospace';
   1103 +    water.style.textShadow = '2px 2px 5px #111717';
   1104 +    document.querySelector('#map_canvas').appendChild(water);
   1105 +  }, time);
   1106  }
   1107  
   1108  /**
   1109   * Main function.
   1110   */
   1111  function main() {
   1112 -  system.stdout.writeLine('main...');
   1113 -  if (true){
   1114 -    page.evaluate(function() {
   1115 -      if (document.getElementById('watermark-ice')) {
   1116 -        var oldStamp = document.getElementById('watermark-ice');
   1117 -        oldStamp.parentNode.removeChild(oldStamp);
   1118 -      }
   1119 -    });
   1120 -  }
   1121 +  page.evaluate(function() {
   1122 +    if (document.getElementById('watermark-ice')) {
   1123 +      var oldStamp = document.getElementById('watermark-ice');
   1124 +      oldStamp.parentNode.removeChild(oldStamp);
   1125 +    }
   1126 +  });
   1127    humanPresence();
   1128    window.setTimeout(function() {
   1129      addTimestamp(getDateTime(0));