diff --git a/README.md b/README.md index 578bb9cc..2f8f9fd3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # ksp-submission This repository is created for Karnataka State Police Hackathon 2023 - submission collection. ## Team Information -### Team Name - +### Team Name - Runtime Terror ### Problem Statement - +Solution for Crowdsourcing of data diff --git a/SolutionToCrowdSourcingData/Temp/Abstract-Runtimeterror-Crowd_Sourcing_of_Records.pdf b/SolutionToCrowdSourcingData/Temp/Abstract-Runtimeterror-Crowd_Sourcing_of_Records.pdf new file mode 100644 index 00000000..a0d05cf1 Binary files /dev/null and b/SolutionToCrowdSourcingData/Temp/Abstract-Runtimeterror-Crowd_Sourcing_of_Records.pdf differ diff --git a/SolutionToCrowdSourcingData/Temp/bin/temp (1).docx b/SolutionToCrowdSourcingData/Temp/bin/temp (1).docx new file mode 100644 index 00000000..a11fecf7 Binary files /dev/null and b/SolutionToCrowdSourcingData/Temp/bin/temp (1).docx differ diff --git a/SolutionToCrowdSourcingData/Temp/bin/temp (1).pptx b/SolutionToCrowdSourcingData/Temp/bin/temp (1).pptx new file mode 100644 index 00000000..d2b05bc5 Binary files /dev/null and b/SolutionToCrowdSourcingData/Temp/bin/temp (1).pptx differ diff --git a/SolutionToCrowdSourcingData/Temp/bin/temp (2).pptx b/SolutionToCrowdSourcingData/Temp/bin/temp (2).pptx new file mode 100644 index 00000000..dd75254a Binary files /dev/null and b/SolutionToCrowdSourcingData/Temp/bin/temp (2).pptx differ diff --git a/SolutionToCrowdSourcingData/flask/main.py b/SolutionToCrowdSourcingData/flask/main.py new file mode 100644 index 00000000..299897ec --- /dev/null +++ b/SolutionToCrowdSourcingData/flask/main.py @@ -0,0 +1,6 @@ +from webapp import create_app + +app = create_app() + +if __name__ == '__main__': + app.run(debug=True) \ No newline at end of file diff --git a/SolutionToCrowdSourcingData/flask/webapp/__init__.py b/SolutionToCrowdSourcingData/flask/webapp/__init__.py new file mode 100644 index 00000000..b0569a14 --- /dev/null +++ b/SolutionToCrowdSourcingData/flask/webapp/__init__.py @@ -0,0 +1,7 @@ +from flask import Flask + +def create_app(): + app = Flask(__name__) + from .views import views + app.register_blueprint(views, url_prefix='/') + return app diff --git a/SolutionToCrowdSourcingData/flask/webapp/__pycache__/__init__.cpython-310.pyc b/SolutionToCrowdSourcingData/flask/webapp/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..84d46f50 Binary files /dev/null and b/SolutionToCrowdSourcingData/flask/webapp/__pycache__/__init__.cpython-310.pyc differ diff --git a/SolutionToCrowdSourcingData/flask/webapp/__pycache__/views.cpython-310.pyc b/SolutionToCrowdSourcingData/flask/webapp/__pycache__/views.cpython-310.pyc new file mode 100644 index 00000000..e9fac3bc Binary files /dev/null and b/SolutionToCrowdSourcingData/flask/webapp/__pycache__/views.cpython-310.pyc differ diff --git a/SolutionToCrowdSourcingData/flask/webapp/templates/end.html b/SolutionToCrowdSourcingData/flask/webapp/templates/end.html new file mode 100644 index 00000000..85e96e04 --- /dev/null +++ b/SolutionToCrowdSourcingData/flask/webapp/templates/end.html @@ -0,0 +1,13 @@ + + + + + + + Thank You + + +

Thank You>

+ + + \ No newline at end of file diff --git a/SolutionToCrowdSourcingData/flask/webapp/templates/input.html b/SolutionToCrowdSourcingData/flask/webapp/templates/input.html new file mode 100644 index 00000000..cb950196 --- /dev/null +++ b/SolutionToCrowdSourcingData/flask/webapp/templates/input.html @@ -0,0 +1,44 @@ + + + + + + + Crowd Sourcing + + + + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+ +
+
+ + + + + \ No newline at end of file diff --git a/SolutionToCrowdSourcingData/flask/webapp/views.py b/SolutionToCrowdSourcingData/flask/webapp/views.py new file mode 100644 index 00000000..329d0eb7 --- /dev/null +++ b/SolutionToCrowdSourcingData/flask/webapp/views.py @@ -0,0 +1,32 @@ +from flask import Blueprint, render_template, request, flash, redirect, url_for +views = Blueprint('views', __name__) + +# import sys +# sys.path.append('../../scraper') +# import os +# os.path.dirname() +# from scraper import htmlCreate + +# from scraper.htmlCreate import createHtml +# from ..scraper import htmlCreate + +@views.route('/', methods=['GET', 'POST']) +def home(): + if request.method == 'POST': + try: + name = request.form.get('name') + email = request.form.get('email-addr') + social = request.form.get('social-handle') + phone = request.form.get('phone-number') + print(name,email,social,phone) + # htmlCreate.downloader(socials + return redirect(url_for('views.end')) + except: + print("Error Occurred") + else: + pass + return render_template("input.html") + +@views.route('/tata') +def end(): + return render_template('end.html') \ No newline at end of file diff --git a/SolutionToCrowdSourcingData/scraper/Blackbird_usecase.py b/SolutionToCrowdSourcingData/scraper/Blackbird_usecase.py new file mode 100644 index 00000000..2481bb6b --- /dev/null +++ b/SolutionToCrowdSourcingData/scraper/Blackbird_usecase.py @@ -0,0 +1,7 @@ +import os +def scrapeBlackBird(username): + try: + os.system("python blackbird.py -u "+username) + return 0 + except: + return -1 \ No newline at end of file diff --git a/SolutionToCrowdSourcingData/scraper/Procfile b/SolutionToCrowdSourcingData/scraper/Procfile new file mode 100644 index 00000000..a1a845fe --- /dev/null +++ b/SolutionToCrowdSourcingData/scraper/Procfile @@ -0,0 +1 @@ +web: gunicorn webserver:app diff --git a/SolutionToCrowdSourcingData/scraper/__init__.py b/SolutionToCrowdSourcingData/scraper/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/SolutionToCrowdSourcingData/scraper/blackbird.py b/SolutionToCrowdSourcingData/scraper/blackbird.py new file mode 100644 index 00000000..c7266035 --- /dev/null +++ b/SolutionToCrowdSourcingData/scraper/blackbird.py @@ -0,0 +1,187 @@ +import argparse +import asyncio +import json +import os +import random +import subprocess +import sys +import time +import warnings +from datetime import datetime + +import aiohttp +from bs4 import BeautifulSoup +from colorama import Fore, init + +file = open('data.json') +searchData = json.load(file) +currentOs = sys.platform +path = os.path.dirname(__file__) +warnings.filterwarnings('ignore') + +useragents = open('useragents.txt').read().splitlines() +proxy = None + + +async def findUsername(username, interfaceType): + start_time = time.time() + timeout = aiohttp.ClientTimeout(total=20) + + print(f"{Fore.LIGHTYELLOW_EX}[!] Searching '{username}' across {len(searchData['sites'])} social networks\033[0m") + + async with aiohttp.ClientSession(timeout=timeout) as session: + tasks = [] + for u in searchData["sites"]: + task = asyncio.ensure_future(makeRequest(session, u, username, interfaceType)) + tasks.append(task) + + results = await asyncio.gather(*tasks) + now = datetime.now().strftime("%m/%d/%Y %H:%M:%S") + executionTime = round(time.time() - start_time, 1) + userJson = {"search-params": {"username": username, "sites-number": len(searchData['sites']), "date": now, "execution-time": executionTime}, "sites": []} + for x in results: + userJson["sites"].append(x) + pathSave = os.path.join(path, 'results', username + '.json') + userFile = open(pathSave, 'w') + json.dump(userJson, userFile, indent=4, sort_keys=True) + + print(f"{Fore.LIGHTYELLOW_EX}[!] Search complete in {executionTime} seconds\033[0m") + print(f"{Fore.LIGHTYELLOW_EX}[!] Results saved to {username}.json\033[0m") + return userJson + + +async def makeRequest(session, u, username, interfaceType): + url = u["url"].format(username=username) + jsonBody = None + useragent = random.choice(useragents) + headers = { + "User-Agent": useragent + } + metadata = [] + if 'headers' in u: + headers.update(eval(u['headers'])) + if 'json' in u: + jsonBody = u['json'].format(username=username) + jsonBody = json.loads(jsonBody) + try: + async with session.request(u["method"], url, json=jsonBody,proxy=proxy, headers=headers, ssl=False) as response: + responseContent = await response.text() + if 'content-type' in response.headers and "application/json" in response.headers["Content-Type"]: + jsonData = await response.json() + else: + soup = BeautifulSoup(responseContent, 'html.parser') + + if eval(u["valid"]): + print(f'{Fore.LIGHTGREEN_EX}[+]\033[0m - #{u["id"]} {Fore.BLUE}{u["app"]}\033[0m {Fore.LIGHTGREEN_EX}account found\033[0m - {Fore.YELLOW}{url}\033[0m [{response.status} {response.reason}]\033[0m') + if 'metadata' in u: + metadata = [] + for d in u["metadata"]: + try: + value = eval(d['value']).strip('\t\r\n') + print(f" |--{d['key']}: {value}") + metadata.append({"type": d["type"], "key": d['key'], "value": value}) + except Exception as e: + pass + return ({"id": u["id"], "app": u['app'], "url": url, "response-status": f"{response.status} {response.reason}", "status": "FOUND", "error-message": None, "metadata": metadata}) + else: + if interfaceType == 'CLI': + if showAll: + print(f'[-]\033[0m - #{u["id"]} {Fore.BLUE}{u["app"]}\033[0m account not found - {Fore.YELLOW}{url}\033[0m [{response.status} {response.reason}]\033[0m') + return ({"id": u["id"], "app": u['app'], "url": url, "response-status": f"{response.status} {response.reason}", "status": "NOT FOUND", "error-message": None, "metadata": metadata}) + except Exception as e: + if interfaceType == 'CLI': + if showAll: + print(f'{Fore.RED}[X]\033[0m - #{u["id"]} {Fore.BLUE}{u["app"]}\033[0m error on request ({repr(e)})- {Fore.YELLOW}{url}\033[0m') + return ({"id": u["id"], "app": u['app'], "url": url, "response-status": None, "status": "ERROR", "error-message": repr(e), "metadata": metadata}) + + +def list_sites(): + for i, u in enumerate(searchData["sites"], 1): + print(f'{i}. {u["app"]}') + + +def read_results(file): + try: + pathRead = os.path.join(path, 'results', file) + f = open(pathRead, 'r') + jsonD = json.load(f) + print(f'Loaded results file: {file}') + print(f"Username: {jsonD['search-params']['username']}") + print(f"Number of sites: {jsonD['search-params']['sites-number']}") + print(f"Date: {jsonD['search-params']['date']}") + print('-------------------------------------------------') + for u in jsonD['sites']: + if u['status'] == "FOUND": + print(f'{Fore.LIGHTGREEN_EX}[+]\033[0m - {Fore.BLUE}{u["app"]}\033[0m {Fore.LIGHTGREEN_EX}account found\033[0m - {Fore.YELLOW}{u["url"]}\033[0m [{u["response-status"]}]\033[0m') + if u["metadata"]: + for d in u["metadata"]: + print(f" |--{d['key']}: {d['value']}") + elif u['status'] == "ERROR": + print(f'{Fore.RED}[X]\033[0m - {Fore.BLUE}{u["app"]}\033[0m error on request ({u["error-message"]}) - {Fore.YELLOW}{u["url"]}\033[0m') + elif u['status'] == "NOT FOUND": + print(f'{Fore.WHITE}[-]\033[0m - {Fore.BLUE}{u["app"]}\033[0m account not found - {Fore.YELLOW}{u["url"]}\033[0m [{u["response-status"]}]\033[0m') + + except Exception as e: + print(f'{Fore.RED}[X] Error reading file [{repr(e)}]') + + +if __name__ == '__main__': + init() + + print(Fore.RED + """ + ▄▄▄▄ ██▓ ▄▄▄ ▄████▄ ██ ▄█▀ ▄▄▄▄ ██▓ ██▀███ ▓█████▄ + ▓█████▄ ▓██▒ ▒████▄ ▒██▀ ▀█ ██▄█▒ ▓█████▄ ▓██▒▓██ ▒ ██▒▒██▀ ██▌ + ▒██▒ ▄██▒██░ ▒██ ▀█▄ ▒▓█ ▄ ▓███▄░ ▒██▒ ▄██▒██▒▓██ ░▄█ ▒░██ █▌ + ▒██░█▀ ▒██░ ░██▄▄▄▄██ ▒▓▓▄ ▄██▒▓██ █▄ ▒██░█▀ ░██░▒██▀▀█▄ ░▓█▄ ▌ + ░▓█ ▀█▓░██████▒▓█ ▓██▒▒ ▓███▀ ░▒██▒ █▄░▓█ ▀█▓░██░░██▓ ▒██▒░▒████▓ + ░▒▓███▀▒░ ▒░▓ ░▒▒ ▓▒█░░ ░▒ ▒ ░▒ ▒▒ ▓▒░▒▓███▀▒░▓ ░ ▒▓ ░▒▓░ ▒▒▓ ▒ + ▒░▒ ░ ░ ░ ▒ ░ ▒ ▒▒ ░ ░ ▒ ░ ░▒ ▒░▒░▒ ░ ▒ ░ ░▒ ░ ▒░ ░ ▒ ▒ + ░ ░ ░ ░ ░ ▒ ░ ░ ░░ ░ ░ ░ ▒ ░ ░░ ░ ░ ░ ░ + ░ ░ ░ ░ ░░ ░ ░ ░ ░ ░ ░ ░ + ░ ░ ░ ░ + + Made with ❤️️ by """ + Fore.BLUE + "p1ngul1n0\n") + + parser = argparse.ArgumentParser(description='An OSINT tool to search for accounts by username in social networks.') + parser.add_argument('-u', action='store', dest='username', + required=False, + help='The target username.') + parser.add_argument('--list-sites', action='store_true', dest='list', + required=False, + help='List all sites currently supported.') + parser.add_argument('-f', action='store', dest='file', + required=False, + help='Read results file.') + parser.add_argument('--web', action='store_true', dest='web', + required=False, + help='Run webserver.') + parser.add_argument('--proxy', action='store', dest='proxy', + required=False, + help='Proxy to send requests through.E.g: --proxy http://127.0.0.1:8080 ') + parser.add_argument('--show-all', action='store_true', dest='showAll', + required=False, + help='Show all results.') + arguments = parser.parse_args() + + if arguments.proxy: + proxy = arguments.proxy + showAll = False + if arguments.showAll: + showAll = arguments.showAll + + if arguments.web: + print('[!] Started WebServer on http://127.0.0.1:9797/') + command = subprocess.run((sys.executable, "webserver.py")) + command.check_returncode() + + if arguments.username: + try: + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + except: + pass + interfaceType = 'CLI' + asyncio.run(findUsername(arguments.username, interfaceType)) + elif arguments.list: + list_sites() + elif arguments.file: + read_results(arguments.file) diff --git a/SolutionToCrowdSourcingData/scraper/callermodule.py b/SolutionToCrowdSourcingData/scraper/callermodule.py new file mode 100644 index 00000000..1548efd3 --- /dev/null +++ b/SolutionToCrowdSourcingData/scraper/callermodule.py @@ -0,0 +1,4 @@ +from Blackbird_usecase import scrapeBlackBird +import filter_black_bird +#scrapeBlackBird("elonmusk") +filter_black_bird.filter_BB("elonmusk") \ No newline at end of file diff --git a/SolutionToCrowdSourcingData/scraper/data.json b/SolutionToCrowdSourcingData/scraper/data.json new file mode 100644 index 00000000..6c6655e7 --- /dev/null +++ b/SolutionToCrowdSourcingData/scraper/data.json @@ -0,0 +1,4964 @@ +{ + "sites": [ + { + "app": "Facebook", + "id": 1, + "method": "GET", + "url": "https://www.facebook.com/{username}", + "valid": "response.status == 200" + }, + { + "app": "YouTube", + "id": 2, + "method": "GET", + "url": "https://www.youtube.com/user/{username}", + "valid": "response.status == 200 and 'name\" content=' in responseContent" + }, + { + "app": "Twitter", + "id": 3, + "metadata": [ + { + "key": "Name", + "type": "generic-data", + "value": "soup.find('a', class_='profile-card-fullname')['title']" + }, + { + "key": "Bio", + "type": "generic-data", + "value": "soup.find('div',class_='profile-bio').string" + }, + { + "key": "Site", + "type": "generic-data", + "value": "soup.find('div',class_='profile-website').text.strip('\\t\\r\\n')" + }, + { + "key": "Member since", + "type": "generic-data", + "value": "soup.find('div',class_='profile-joindate').find('span')['title']" + }, + { + "key": "picture", + "type": "image", + "value": "'https://nitter.net'+soup.find('a', class_='profile-card-avatar')['href']" + }, + { + "key": "location", + "type": "location", + "value": "soup.select_one('.profile-location:nth-of-type(2)').text.strip('\\t\\r\\n')" + } + ], + "method": "GET", + "url": "https://nitter.net/{username}", + "valid": "response.status == 200 and ') | nitter' in responseContent" + }, + { + "app": "Telegram", + "id": 4, + "metadata": [ + { + "key": "Name", + "type": "generic-data", + "value": "soup.find('span', dir='auto').string" + }, + { + "key": "picture", + "type": "image", + "value": "soup.find('img', class_='tgme_page_photo_image')['src']" + } + ], + "method": "GET", + "url": "https://t.me/{username}", + "valid": "'You can contact' in soup.find('meta', property='og:description')['content'] and 'tgme_page_title' in responseContent" + }, + { + "app": "TikTok", + "id": 5, + "metadata": [ + { + "key": "Name", + "type": "generic-data", + "value": "soup.find('h1').text" + }, + { + "key": "Bio", + "type": "generic-data", + "value": "soup.find('h2', attrs={'data-e2e':'user-bio'}).text" + }, + { + "key": "picture", + "type": "image", + "value": "soup.find('img')['src']" + } + ], + "method": "GET", + "url": "https://www.tiktok.com/@{username}", + "valid": "response.status == 200 and 'title=\"Following\"' in responseContent" + }, + { + "app": "Tinder", + "id": 6, + "metadata": [ + { + "key": "Name", + "type": "generic-data", + "value": "soup.find('meta', property='profile:first_name')['content']" + }, + { + "key": "picture", + "type": "image", + "value": "soup.find('meta', property='og:image')['content']" + } + ], + "method": "GET", + "url": "https://tinder.com/@{username}", + "valid": "'@' in soup.find('meta', property='og:title')['content'] and ') | Tinder' in responseContent" + }, + { + "app": "Reddit", + "id": 10, + "metadata": [ + { + "key": "Name", + "type": "generic-data", + "value": "jsonData['data']['subreddit']['title']" + }, + { + "key": "Bio", + "type": "generic-data", + "value": "jsonData['data']['subreddit']['public_description']" + }, + { + "key": "picture", + "type": "image", + "value": "jsonData['data']['snoovatar_img']" + } + ], + "method": "GET", + "url": "https://www.reddit.com/user/{username}/about.json", + "valid": "response.status == 200 and 'total_karma' in responseContent" + }, + { + "app": "Soundcloud", + "id": 11, + "metadata": [ + { + "key": "Name", + "type": "generic-data", + "value": "soup.find('header').find('p').string" + }, + { + "key": "location", + "type": "location", + "value": "soup.find('meta',property='og:locality')['content']+', '+soup.find('meta',property='og:country-name')['content']" + }, + { + "key": "picture", + "type": "image", + "value": "soup.find('meta',property='twitter:image')['content']" + } + ], + "method": "GET", + "url": "https://soundcloud.com/{username}", + "valid": "response.status == 200" + }, + { + "app": "Github", + "id": 12, + "metadata": [ + { + "key": "Name", + "type": "generic-data", + "value": "soup.find('span',class_='p-name').text.strip('\\t\\r\\n')" + }, + { + "key": "Nickname", + "type": "generic-data", + "value": "soup.find('span',class_='p-nickname').text.strip('\\t\\r\\n')" + }, + { + "key": "Site", + "type": "generic-data", + "value": "soup.find('a',rel='nofollow me').text" + }, + { + "key": "location", + "type": "location", + "value": "soup.find('span',class_='p-label').text" + }, + { + "key": "picture", + "type": "image", + "value": "soup.find('meta',property='og:image')['content']" + } + ], + "method": "GET", + "url": "https://github.com/{username}", + "valid": "response.status == 200" + }, + { + "app": "Steam", + "id": 13, + "metadata": [ + { + "key": "Name", + "type": "generic-data", + "value": "soup.find('div',class_='header_real_name').find('bdi').text" + }, + { + "key": "Nickname", + "type": "generic-data", + "value": "soup.find('span',class_='actual_persona_name').string" + }, + { + "key": "Bio", + "type": "generic-data", + "value": "soup.find('meta',property='og:description')['content']" + }, + { + "key": "location", + "type": "location", + "value": "soup.find('img',class_='profile_flag').next_sibling.strip('\\t\\r\\n')" + }, + { + "key": "picture", + "type": "image", + "value": "soup.find('link',rel='image_src')['href']" + } + ], + "method": "GET", + "url": "https://steamcommunity.com/id/{username}/", + "valid": "'Error' not in soup.find('title').string and 'g_rgProfileData =' in responseContent" + }, + { + "app": "Linktree", + "id": 14, + "metadata": [ + { + "key": "Name", + "type": "generic-data", + "value": "soup.find('h1').string" + }, + { + "key": "Description", + "type": "generic-data", + "value": "soup.find('meta',attrs={'name':'description'})['content']" + }, + { + "key": "picture", + "type": "image", + "value": "soup.find('meta',property='og:image')['content']" + } + ], + "method": "GET", + "url": "https://linktr.ee/{username}", + "valid": "response.status == 200" + }, + { + "app": "Xbox Gamertag", + "id": 15, + "metadata": [ + { + "key": "Name", + "type": "generic-data", + "value": "soup.find('h1').string" + }, + { + "key": "picture", + "type": "image", + "value": "'https:' + soup.find('img',class_='rounded img-thumbnail')['src']" + } + ], + "method": "GET", + "url": "https://www.xboxgamertag.com/search/{username}", + "valid": "response.status == 200 and 'Games Played' in responseContent" + }, + { + "app": "Twitter Archived", + "id": 16, + "method": "GET", + "url": "http://archive.org/wayback/available?url=https://twitter.com/{username}", + "valid": "'available' in responseContent" + }, + { + "app": "Xvideos", + "id": 17, + "metadata": [ + { + "key": "Name", + "type": "generic-data", + "value": "soup.find('div',id='profile-title').find('strong').text" + }, + { + "key": "Gender", + "type": "generic-data", + "value": "soup.find('p',id='pinfo-sex').find('span').text" + }, + { + "key": "Age", + "type": "generic-data", + "value": "soup.find('p',id='pinfo-age').find('span').text" + }, + { + "key": "Member since", + "type": "generic-data", + "value": "soup.find('p',id='pinfo-signedup').find('span').text" + }, + { + "key": "location", + "type": "location", + "value": "soup.find('p',id='pinfo-city').find('span').text+', '+soup.find('p',id='pinfo-country').find('span').text" + }, + { + "key": "picture", + "type": "image", + "value": "soup.find('img')['src']" + } + ], + "method": "GET", + "url": "https://www.xvideos.com/profiles/{username}", + "valid": "response.status == 200" + }, + { + "app": "PornHub", + "id": 18, + "metadata": [ + { + "key": "Name", + "type": "generic-data", + "value": "soup.find('div',class_='profileUserName').find('a').string" + }, + { + "key": "Gender", + "type": "generic-data", + "value": "soup.find('dl',class_='moreInformation').select_one('dd:nth-of-type(1)').string" + }, + { + "key": "Last login", + "type": "generic-data", + "value": "soup.find('dl',class_='moreInformation').select_one('dd:nth-of-type(2)').string" + }, + { + "key": "Relationship Status", + "type": "generic-data", + "value": "soup.find('dl',class_='moreInformation').select_one('dd:nth-of-type(3)').string" + }, + { + "key": "Interested In", + "type": "generic-data", + "value": "soup.find('dl',class_='moreInformation').select_one('dd:nth-of-type(4)').string" + }, + { + "key": "location", + "type": "location", + "value": "soup.find('dl',class_='moreInformation').select_one('dd:nth-of-type(5)').string+', '+soup.find('dl',class_='moreInformation').select_one('dd:nth-of-type(6)').string" + }, + { + "key": "picture", + "type": "image", + "value": "soup.find('img',id='getAvatar')['src']" + } + ], + "method": "GET", + "url": "https://www.pornhub.com/users/{username}", + "valid": "response.status == 200" + }, + { + "app": "Xhamster", + "id": 19, + "metadata": [ + { + "key": "Name", + "type": "generic-data", + "value": "soup.find('div',class_='user-name').text.strip('\\t\\r\\n')" + }, + { + "key": "Gender", + "type": "generic-data", + "value": "soup.find('div',class_='i-am').find(class_='value').text" + }, + { + "key": "Last login", + "type": "generic-data", + "value": "soup.find('div',class_='offline').text" + }, + { + "key": "location", + "type": "location", + "value": "soup.find('div',class_='from').find(class_='value').text" + }, + { + "key": "picture", + "type": "image", + "value": "soup.find('img',class_='xh-avatar')['src']" + } + ], + "method": "GET", + "url": "https://xhamster.com/users/{username}", + "valid": "response.status == 200" + }, + { + "app": "Periscope", + "id": 20, + "method": "GET", + "url": "https://www.periscope.tv/{username}", + "valid": "response.status == 200 and '