__init__.py (22403B)
1 from bs4 import BeautifulSoup 2 import requests 3 import json 4 import asyncio, io, logging, os, tempfile 5 import plugins 6 import re 7 8 logger = logging.getLogger(__name__) 9 10 11 def _initialise(bot): 12 plugins.register_user_command(["intel", "iitc", "portalpic", "portalinfo", "active_iitcplugins"]) 13 plugins.register_admin_command(["setintel", "show_iitcplugins", "set_iitcplugins"]) 14 _get_iitc_plugins(bot) 15 16 17 @asyncio.coroutine 18 def _open_file(name, otype): 19 return open(name, otype) 20 21 @asyncio.coroutine 22 def readline(f): 23 while True: 24 data = f.readline() 25 if data: 26 return data 27 28 def _parse_onlineRepos(url, ext=''): 29 page = requests.get(url).text 30 if 'gitlab.com' in url: 31 files = [] 32 for json_page in json.loads(page): 33 for attribute, value in json_page.items(): 34 if attribute == "name": 35 if value.endswith(ext): 36 files.append(url.replace("/tree/", "/blobs/master") + "&filepath=" + value) 37 return files 38 elif 'github.com' in url: 39 files = [] 40 for attribute, value in json.loads(page).items(): 41 if attribute == "tree": 42 for tree in value: 43 for attribute, value in tree.items(): 44 if attribute == "path": 45 if value.endswith(ext) and not 'total-conversion-build.user.js' in value and not 'user-location.user.js' in value and not 'test' in value: 46 files.append(url.replace("https://api.github.com/repos/", "https://raw.githubusercontent.com/").replace("git/trees/",'').replace("master?recursive=1","master/") + value) 47 return files 48 else: 49 soup = BeautifulSoup(page, 'html.parser') 50 return [url + '/' + node.get('href') for node in soup.find_all('a') if node.get('href').endswith(ext)] 51 52 def _get_iitc_plugins(bot): 53 logger.debug("getting availible plugins") 54 if bot.config.exists(["intel_screenbot", "gitlab_token"]): 55 token = bot.config.get_by_path(["intel_screenbot", "gitlab_token"]) 56 url_config = bot.config.get_by_path(["intel_screenbot", "plugin_dirs"]) 57 ext = '.user.js' 58 data=[] 59 for url in url_config: 60 if ext in url: 61 item = {"name": url.split('/', url.count('/'))[-1].replace(ext, ''), "url": url} 62 data.append(item) 63 else: 64 if "gitlab.com" in url: 65 url = url + '?private_token=' + token 66 for file in _parse_onlineRepos(url, ext): 67 if "gitlab.com" in url: 68 item = {"name": file.split('=', file.count('='))[-1].replace(ext, ''), "url": file} 69 elif 'github.com' in url: 70 item = {"name": file.split('/', file.count('/'))[-1].replace(ext, ''), "url": file} 71 else: 72 item = {"name": file.split('/', file.count('/'))[-1].replace(ext, ''), "url": file} 73 data.append(item) 74 75 iitc_plugins = data 76 if bot.memory.exists(["iitc_plugins"]): 77 bot.memory.pop_by_path(["iitc_plugins"]) 78 bot.memory.set_by_path(["iitc_plugins"], iitc_plugins) 79 else: 80 bot.memory.set_by_path(["iitc_plugins"], iitc_plugins) 81 82 def _get_lines(shell_command): 83 p = yield from asyncio.create_subprocess_shell(shell_command) 84 output = yield from asyncio.wait_for(p.wait(), 240.0) 85 return p.returncode, output, True 86 87 @asyncio.coroutine 88 def _screencap(url, args_filepath, filepath, filename, bot, event, arguments): 89 os.chmod(filepath, 0o666) 90 loop = asyncio.get_event_loop() 91 command = 'phantomjs hangupsbot/plugins/intel_screenbot/screencap.js "' + args_filepath + '"' 92 task = _get_lines(command) 93 task = asyncio.wait_for(task, 420.0) 94 exitcode, output, status = yield from task 95 96 # read the resulting file into a byte array 97 # yield from asyncio.sleep(10) 98 if (arguments['screenshotfunction'] != 'portalinfoText'): 99 file_resource = yield from _open_file(filepath, 'rb') 100 file_data = yield from loop.run_in_executor(None, file_resource.read) 101 image_data = yield from loop.run_in_executor(None, io.BytesIO, file_data) 102 if status: 103 try: 104 image_id = yield from bot._client.upload_image(image_data, filename=filename) 105 yield from bot.coro_send_message(event.conv.id_, None, image_id=image_id) 106 os.unlink(filepath) 107 os.unlink(args_filepath) 108 except Exception as e: 109 os.unlink(filepath) 110 os.unlink(args_filepath) 111 logger.exception("upload failed: {}".format(url)) 112 logger.exception("exception: {}".format(e)) 113 yield from bot.coro_send_message(event.conv_id, "<i>error uploading screenshot</i>") 114 else: 115 if status: 116 response_file = yield from _open_file(arguments['portalinfoResponse'], 'r') 117 response = yield from readline(response_file) 118 yield from bot.coro_send_message(event.conv.id_, response.replace("'", "")) 119 os.unlink(filepath) 120 os.unlink(args_filepath) 121 os.unlink(arguments['portalinfoResponse']) 122 123 def setintel(bot, event, *args): 124 """set url for current converation for the intel or iitc command. 125 use /bot clearintel to clear the previous url before setting a new one.""" 126 127 url = bot.conversation_memory_get(event.conv_id, 'IntelURL') 128 if url is None: 129 bot.conversation_memory_set(event.conv_id, 'IntelURL', ''.join(args)) 130 html = "<i><b>{}</b> updated screenshot URL".format(event.user.full_name) 131 yield from bot.coro_send_message(event.conv, html) 132 133 else: 134 bot.conversation_memory_set(event.conv_id, 'IntelURL', None) 135 bot.conversation_memory_set(event.conv_id, 'IntelURL', ''.join(args)) 136 html = "<i><b>{}</b> updated screenshot URL".format(event.user.full_name) 137 yield from bot.coro_send_message(event.conv, html) 138 139 def portalpic(bot, event, *args): 140 """get a screenshot of the portalInfo by URL.""" 141 142 arguments = {} 143 144 if args: 145 if len(args) > 1: 146 url = ' '.join(str(i) for i in args) 147 else: 148 url = args[0] 149 if '"' in url: 150 url = url.replace('"', '') 151 else: 152 html = "<i><b>{}</b> No Arguments provided".format(event.user.full_name) 153 yield from bot.coro_send_message(event.conv, html) 154 155 if bot.config.exists(["intel_screenbot", "email"]): 156 if bot.config.exists(["intel_screenbot", "password"]): 157 email = bot.config.get_by_path(["intel_screenbot", "email"]) 158 password = bot.config.get_by_path(["intel_screenbot", "password"]) 159 arguments['email'] = email 160 arguments['password'] = password 161 else: 162 html = "<i><b>{}</b> No Intel password has been added to config. Unable to authenticate".format(event.user.full_name) 163 yield from bot.coro_send_message(event.conv, html) 164 else: 165 html = "<i><b>{}</b> No Intel Email/password has been added to config. Unable to authenticate".format(event.user.full_name) 166 yield from bot.coro_send_message(event.conv, html) 167 168 if url is None: 169 html = "<i><b>{}</b> No Portal URL has been set for screenshots.".format(event.user.full_name) 170 yield from bot.coro_send_message(event.conv, html) 171 else: 172 if re.match(r'(http(s)?:\/\/)', url): 173 yield from bot.coro_send_message(event.conv_id, "<i>Portal Picture requested, please wait...</i>") 174 filepath = tempfile.NamedTemporaryFile(suffix=".png", delete=False).name 175 filename = filepath.split('/', filepath.count('/'))[-1] 176 args_filepath = tempfile.NamedTemporaryFile(prefix="args_{}".format(event.conv_id), suffix=".json", delete=False).name 177 178 arguments['url'] = url 179 arguments['filepath'] = filepath 180 arguments['screenshotfunction'] = "portalinfoScreen" 181 182 with open(args_filepath, 'w') as out: 183 out.write(json.dumps(arguments)) 184 185 try: 186 loop = asyncio.get_event_loop() 187 image_data = yield from _screencap(url, args_filepath, filepath, filename, bot, event, arguments) 188 except Exception as e: 189 yield from bot.coro_send_message(event.conv_id, "<i>error getting screenshot</i>") 190 logger.exception("screencap failed".format(url)) 191 return 192 else: 193 html = "<i><b>{}</b> No URL found in Arguments".format(event.user.full_name) 194 yield from bot.coro_send_message(event.conv, html) 195 196 def portalinfo(bot, event, *args): 197 """get a of the portalInfo by URL.""" 198 199 arguments = {} 200 201 if args: 202 if len(args) > 1: 203 url = ' '.join(str(i) for i in args) 204 else: 205 url = args[0] 206 if '"' in url: 207 url = url.replace('"', '') 208 else: 209 html = "<i><b>{}</b> No Arguments provided".format(event.user.full_name) 210 yield from bot.coro_send_message(event.conv, html) 211 212 if bot.config.exists(["intel_screenbot", "email"]): 213 if bot.config.exists(["intel_screenbot", "password"]): 214 email = bot.config.get_by_path(["intel_screenbot", "email"]) 215 password = bot.config.get_by_path(["intel_screenbot", "password"]) 216 arguments['email'] = email 217 arguments['password'] = password 218 else: 219 html = "<i><b>{}</b> No Intel password has been added to config. Unable to authenticate".format(event.user.full_name) 220 yield from bot.coro_send_message(event.conv, html) 221 else: 222 html = "<i><b>{}</b> No Intel Email/password has been added to config. Unable to authenticate".format(event.user.full_name) 223 yield from bot.coro_send_message(event.conv, html) 224 225 if url is None: 226 html = "<i><b>{}</b> No Portal URL has been set for portal Info.".format(event.user.full_name) 227 yield from bot.coro_send_message(event.conv, html) 228 else: 229 if re.match(r'(http(s)?:\/\/)', url): 230 yield from bot.coro_send_message(event.conv_id, "<i>Portal Info requested, please wait...</i>") 231 filepath = tempfile.NamedTemporaryFile(suffix=".png", delete=False).name 232 portalinfoResponse = tempfile.NamedTemporaryFile(prefix="response_{}".format(event.conv_id), delete=False).name 233 filename = filepath.split('/', filepath.count('/'))[-1] 234 args_filepath = tempfile.NamedTemporaryFile(prefix="args_{}".format(event.conv_id), suffix=".json", delete=False).name 235 236 arguments['url'] = url 237 arguments['filepath'] = filepath 238 arguments['screenshotfunction'] = "portalinfoText" 239 arguments['portalinfoResponse'] = portalinfoResponse 240 241 with open(args_filepath, 'w') as out: 242 out.write(json.dumps(arguments)) 243 244 try: 245 loop = asyncio.get_event_loop() 246 image_data = yield from _screencap(url, args_filepath, filepath, filename, bot, event, arguments) 247 except Exception as e: 248 yield from bot.coro_send_message(event.conv_id, "<i>error getting infos</i>") 249 logger.exception("getting infos failed".format(url)) 250 return 251 else: 252 html = "<i><b>{}</b> No URL found in Arguments".format(event.user.full_name) 253 yield from bot.coro_send_message(event.conv, html) 254 255 def intel(bot, event, *args): 256 """get a screenshot of a search term or intel URL or the default intel URL of the hangout.""" 257 258 arguments = {} 259 260 if args: 261 if len(args) > 1: 262 url = ' '.join(str(i) for i in args) 263 else: 264 url = args[0] 265 if '"' in url: 266 url = url.replace('"', '') 267 else: 268 try: 269 url = bot.conversation_memory_get(event.conv_id, 'IntelURL') 270 except: 271 url = 'https://www.ingress.com/intel' 272 273 if bot.config.exists(["intel_screenbot", "email"]): 274 if bot.config.exists(["intel_screenbot", "password"]): 275 email = bot.config.get_by_path(["intel_screenbot", "email"]) 276 password = bot.config.get_by_path(["intel_screenbot", "password"]) 277 arguments['email'] = email 278 arguments['password'] = password 279 else: 280 html = "<i><b>{}</b> No Intel password has been added to config. Unable to authenticate".format(event.user.full_name) 281 yield from bot.coro_send_message(event.conv, html) 282 else: 283 html = "<i><b>{}</b> No Intel Email/password has been added to config. Unable to authenticate".format(event.user.full_name) 284 yield from bot.coro_send_message(event.conv, html) 285 286 if url is None: 287 html = "<i><b>{}</b> No Intel URL or search term has been set for screenshots.".format(event.user.full_name) 288 yield from bot.coro_send_message(event.conv, html) 289 290 else: 291 if re.match(r'(http(s)?:\/\/)', url): 292 search = 'nix' 293 zoomParameter = re.search(r"(?:&z=)", url, re.IGNORECASE) 294 if zoomParameter: 295 ZoomSearch = re.finditer(r"(?:&z=).*", url, flags=re.I) 296 for matchNum, zoomlevel_raw in enumerate(ZoomSearch): 297 matchNum = matchNum + 1 298 zoomlevel_clean = zoomlevel_raw.group() 299 zoomlevel = zoomlevel_clean[3:][:2] 300 if zoomlevel.isdigit(): 301 yield from bot.coro_send_message(event.conv_id, "<i>intel map at zoom level "+ zoomlevel + " requested, please wait...</i>") 302 else: 303 yield from bot.coro_send_message(event.conv_id, "<i>intel map requested, please wait...</i>") 304 else: 305 search = url 306 zoomParameter = re.search(r"(?<=z=)", url, re.IGNORECASE) 307 if zoomParameter: 308 ZoomSearch = re.finditer(r"(?<=z=)[^\s]+", url, flags=re.I) 309 for matchNum, zoomlevel_raw in enumerate(ZoomSearch): 310 matchNum = matchNum + 1 311 zoomlevel = zoomlevel_raw.group() 312 search = search.replace("z={}".format(zoomlevel),"") 313 if zoomlevel.isdigit(): 314 yield from bot.coro_send_message(event.conv_id, "<i>searching " + search + " on intel map and screenshooting at zoom level "+ zoomlevel + " as requested, please wait...</i>") 315 arguments['zoomlevel'] = str(zoomlevel) 316 else: 317 yield from bot.coro_send_message(event.conv_id, "<i>searching " + search + " on intel map and screenshooting as requested, please wait...</i>") 318 url = 'https://www.ingress.com/intel' 319 320 filepath = tempfile.NamedTemporaryFile(suffix=".png", delete=False).name 321 filename = filepath.split('/', filepath.count('/'))[-1] 322 args_filepath = tempfile.NamedTemporaryFile(prefix="args_{}".format(event.conv_id), suffix=".json", delete=False).name 323 324 arguments['search'] = search 325 arguments['url'] = url 326 arguments['filepath'] = filepath 327 arguments['maptype'] = "intel" 328 arguments['screenshotfunction'] = "map" 329 330 with open(args_filepath, 'w') as out: 331 out.write(json.dumps(arguments)) 332 333 try: 334 loop = asyncio.get_event_loop() 335 image_data = yield from _screencap(url, args_filepath, filepath, filename, bot, event, arguments) 336 except Exception as e: 337 yield from bot.coro_send_message(event.conv_id, "<i>error getting screenshot</i>") 338 logger.exception("screencap failed".format(url)) 339 return 340 341 342 def iitc(bot, event, *args): 343 """get a screenshot of a search term or intel URL or the default intel URL of the hangout.""" 344 345 arguments = {} 346 347 if args: 348 if len(args) > 1: 349 url = ' '.join(str(i) for i in args) 350 else: 351 url = args[0] 352 if '"' in url: 353 url = url.replace('"', '') 354 else: 355 url = bot.conversation_memory_get(event.conv_id, 'IntelURL') 356 357 if bot.config.exists(["intel_screenbot", "email"]): 358 if bot.config.exists(["intel_screenbot", "password"]): 359 email = bot.config.get_by_path(["intel_screenbot", "email"]) 360 password = bot.config.get_by_path(["intel_screenbot", "password"]) 361 arguments['email'] = email 362 arguments['password'] = password 363 else: 364 html = "<i><b>{}</b> No Intel password has been added to config. Unable to authenticate".format(event.user.full_name) 365 yield from bot.coro_send_message(event.conv, html) 366 else: 367 html = "<i><b>{}</b> No Intel Email/password has been added to config. Unable to authenticate".format(event.user.full_name) 368 yield from bot.coro_send_message(event.conv, html) 369 370 if url is None: 371 html = "<i><b>{}</b> No Intel URL has been set for screenshots.".format(event.user.full_name) 372 yield from bot.coro_send_message(event.conv, html) 373 374 else: 375 if re.match(r'(http(s)?:\/\/)', url): 376 search = 'nix' 377 zoomParameter = re.search(r"(?:&z=)", url, re.IGNORECASE) 378 if zoomParameter: 379 ZoomSearch = re.finditer(r"(?:&z=).*", url, flags=re.I) 380 for matchNum, zoomlevel_raw in enumerate(ZoomSearch): 381 matchNum = matchNum + 1 382 zoomlevel_clean = zoomlevel_raw.group() 383 zoomlevel = zoomlevel_clean[3:][:2] 384 if zoomlevel.isdigit(): 385 yield from bot.coro_send_message(event.conv_id, "<i>iitc map at zoom level "+ zoomlevel + " requested, please wait...</i>") 386 else: 387 yield from bot.coro_send_message(event.conv_id, "<i>iitc map requested, please wait...</i>") 388 else: 389 search = url 390 zoomParameter = re.search(r"(?<=z=)", url, re.IGNORECASE) 391 if zoomParameter: 392 ZoomSearch = re.finditer(r"(?<=z=)[^\s]+", url, flags=re.I) 393 for matchNum, zoomlevel_raw in enumerate(ZoomSearch): 394 matchNum = matchNum + 1 395 zoomlevel = zoomlevel_raw.group() 396 search = search.replace("z={}".format(zoomlevel),"") 397 if zoomlevel.isdigit(): 398 yield from bot.coro_send_message(event.conv_id, "<i>searching " + search + " on iitc map and screenshooting it at zoom level "+ zoomlevel + " as requested, please wait...</i>") 399 arguments['zoomlevel'] = str(zoomlevel) 400 else: 401 yield from bot.coro_send_message(event.conv_id, "<i>searching " + search + " on iitc map and and screenshooting it as requested, please wait...</i>") 402 url = 'https://www.ingress.com/intel' 403 404 filepath = tempfile.NamedTemporaryFile(suffix=".png", delete=False).name 405 filename = filepath.split('/', filepath.count('/'))[-1] 406 args_filepath = tempfile.NamedTemporaryFile(prefix="args_{}".format(event.conv_id), suffix=".json", delete=False).name 407 logger.debug("temporary screenshot file: {}".format(filepath)) 408 logger.debug("temporary args file: {}".format(args_filepath)) 409 if bot.conversation_memory_get(event.conv_id, 'iitc_plugins'): 410 plugins = [] 411 plugin_names = bot.conversation_memory_get(event.conv_id, 'iitc_plugins').split(", ") 412 if bot.memory.exists(["iitc_plugins"]): 413 for plugin_objects in bot.memory.get_by_path(["iitc_plugins"]): 414 for plugin_name in plugin_names: 415 if plugin_objects["name"] == plugin_name: 416 plugins.append(plugin_objects["url"]) 417 else: 418 plugins = '' 419 420 arguments['plugins'] = plugins 421 arguments['search'] = search 422 arguments['url'] = url 423 arguments['filepath'] = filepath 424 arguments['maptype'] = "iitc" 425 arguments['screenshotfunction'] = "map" 426 427 with open(args_filepath, 'w') as out: 428 out.write(json.dumps(arguments)) 429 430 try: 431 loop = asyncio.get_event_loop() 432 image_data = yield from _screencap(url, args_filepath, filepath, filename, bot, event, arguments) 433 except Exception as e: 434 yield from bot.coro_send_message(event.conv_id, "<i>error getting screenshot</i>") 435 logger.exception("screencap failed".format(url)) 436 return 437 438 def show_iitcplugins(bot, event, *args): 439 """Shows all available iitc Plugins.""" 440 if bot.memory.exists(["iitc_plugins"]): 441 plugin_names = [] 442 for plugin_objects in bot.memory.get_by_path(["iitc_plugins"]): 443 for attribute, value in plugin_objects.items(): 444 if attribute == "name": 445 plugin_names.append(value) 446 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)) 447 448 def set_iitcplugins(bot, event, *args): 449 """sets the activated iitc plugins for a hangout.""" 450 if not bot.conversation_memory_get(event.conv_id, 'iitc_plugins') is None: 451 bot.conversation_memory_set(event.conv_id, 'iitc_plugins', None) 452 bot.conversation_memory_set(event.conv_id, 'iitc_plugins', ', '.join(args)) 453 html = "<i><b>{}</b> updated plugins".format(event.user.full_name) 454 yield from bot.coro_send_message(event.conv, html) 455 else: 456 bot.conversation_memory_set(event.conv_id, 'iitc_plugins', ', '.join(args)) 457 html = "<i><b>{}</b> updated plugins".format(event.user.full_name) 458 yield from bot.coro_send_message(event.conv, html) 459 460 def active_iitcplugins(bot, event, *args): 461 """shows the activated iitc plugins for a hangout.""" 462 if bot.conversation_memory_get(event.conv_id, 'iitc_plugins') is None: 463 html = "<i><b>{}</b> no active IITC Plugins for this conversation".format(event.user.full_name) 464 yield from bot.coro_send_message(event.conv, html) 465 else: 466 plugins = bot.conversation_memory_get(event.conv_id, 'iitc_plugins') 467 html = "<i><b>{}</b> plugins for this conversation: {}<br />".format(event.user.full_name, plugins) 468 yield from bot.coro_send_message(event.conv, html)