Beispiel #1
0
    def get_token(self):
        # If the token has expired, request a new one
        if self.timeout < int(time.time()):
            # For some dumb reason it has to be a string
            data = {
                "grant_type": "client_credentials",
                "client_id": self.gfyid,
                "client_secret": self.gfysecret
            }
            if self.username:
                data['grant_type'] = 'password'
                data['username'] = self.username
                data['password'] = self.password

            url = "https://api.gfycat.com/v1/oauth/token"
            r = requests.post(url,
                              data=str(data),
                              headers={'User-Agent': consts.user_agent})
            try:
                response = r.json()
            except json.decoder.JSONDecodeError as e:
                print(r.text)
                raise
            self.timeout = int(time.time()) + response["expires_in"]
            self.token = response["access_token"]
            CredentialsLoader.set_credential('gfycat', 'refresh_token',
                                             self.token)
            CredentialsLoader.set_credential('gfycat', 'token_expiration',
                                             str(self.timeout))
        return self.token
Beispiel #2
0
def bind_db(db):
    creds = CredentialsLoader.get_credentials()['database']

    if creds['type'] == 'sqlite':
        db.bind(provider='sqlite',
                filename='../database.sqlite',
                create_db=True)
    elif creds['type'] == 'mysql':
        # Check for SSL arguments
        ssl = {}
        if creds.get('ssl-ca', None):
            ssl['ssl'] = {
                'ca': creds['ssl-ca'],
                'key': creds['ssl-key'],
                'cert': creds['ssl-cert']
            }

        db.bind(provider="mysql",
                host=creds['host'],
                user=creds['username'],
                password=creds['password'],
                db=creds['database'],
                ssl=ssl,
                port=int(creds.get('port', 3306)))
    else:
        raise Exception("No database configuration")

    db.generate_mapping(create_tables=True)
Beispiel #3
0
    def _upload_image(self, file, media_type, nsfw, audio=False):
        file.seek(0)

        api = None
        params = None
        data = {"type": "file"}
        if media_type == consts.MP4 or media_type == consts.WEBM:
            data['video'] = ("video." + media_type, file, "video/" + media_type)
            data['name'] = "video." + media_type
            api = self.UPLOAD
            m = MultipartEncoder(fields=data)
            r = self.post_request(api, m, {'Content-Type': m.content_type})
        # We get around the image file size restriction by using a client ID made by a browser
        # Luckily the API is similarish (rather than last time where it wasn't and also 3 steps)
        elif media_type == consts.GIF:
            s = requests.Session()
            api = self.IMAGE_UPLOAD
            params = {'client_id': CredentialsLoader.get_credentials()[self.CREDENTIALS_BLOCK]['imgur_web_id']}
            r = s.options(self.API_BASE + api, params=params)
            data['image'] = (file.name, file, "image/gif")
            data['name'] = file.name
            m = MultipartEncoder(fields=data)
            r = s.post(self.API_BASE + api, headers={'Content-Type': m.content_type}, data=m, params=params)
        # pprint(r.json())
        j = r.json()
        if not j['data'].get('id', False):
            print(j)
            if j['data'].get('error', False):
                print("Error:", j['data']['error'])
                return None
        return j['data']['id']
Beispiel #4
0
 def __init__(self):
     creds = CredentialsLoader.get_credentials()['gfycat']
     self.gfypath = creds["gfypath"]
     self.gfyid = creds["gfycat_id"]
     self.gfysecret = creds["gfycat_secret"]
     self.username = creds.get('username', None)
     self.password = creds.get('password', None)
     self.token = creds.get('refresh_token', None)
     self.timeout = int(creds.get('token_expiration', 0))
Beispiel #5
0
    def __init__(self):
        creds = CredentialsLoader.get_credentials()[self.CREDENTIALS_BLOCK]
        self.client_id = creds["imgur_id"]
        self.client_secret = creds["imgur_secret"]
        self.access = creds.get('access_token', None)
        self.refresh = creds.get('refresh_token', None)
        self.timeout = int(creds.get('token_expiration', 0))

        if self.refresh is None:
            self.authenticate()
        if self.access is None:
            self.get_token()
Beispiel #6
0
    def __init__(self):
        creds = CredentialsLoader.get_credentials()['gfycat']
        self.gfyid = creds["gfycat_id"]
        self.gfysecret = creds["gfycat_secret"]
        self.access = creds.get('access_token', None)
        self.refresh = creds.get('refresh_token', None)
        self.timeout = int(creds.get('token_expiration', 0))

        if self.refresh is None:
            self.authenticate(True)
        if self.access is None:
            self.get_token()
Beispiel #7
0
    def refresh(self):
        data = {
            'refresh_token': self.refresh_token,
            'client_id': self.client_id,
            'client_secret': self.client_secret,
            'grant_type': 'refresh_token'
        }

        url = API_URL + 'oauth2/token'

        response = requests.post(url, data=data)

        if response.status_code != 200:
            raise ImgurClientError('Error refreshing access token!',
                                   response.status_code)

        response_data = response.json()
        self.current_access_token = response_data['access_token']

        CredentialsLoader.set_credential('imgur', 'access_token',
                                         response_data['access_token'])
Beispiel #8
0
 def get_token(self):
     # If the token has expired, request a new one
     if self.timeout < int(time.time()):
         data = {"grant_type": "refresh", "client_id": self.gfyid,
                 "client_secret": self.gfysecret, "refresh_token": self.refresh}
         url = self.TOKEN_URL
         # For some dumb reason, data has to be a string
         r = requests.post(url, data=str(data), headers={'User-Agent': consts.user_agent})
         try:
             response = r.json()
         except json.decoder.JSONDecodeError as e:
             print(r.text)
             raise
         # Sometimes Gfycat randomly invalidates refresh tokens >:(
         if r.status_code == 401:
             raise InvalidRefreshToken
         self.timeout = int(time.time()) + response["expires_in"]
         self.access = response["access_token"]
         CredentialsLoader.set_credential(self.CREDENTIALS_BLOCK, 'access_token', self.access)
         CredentialsLoader.set_credential(self.CREDENTIALS_BLOCK, 'token_expiration', str(self.timeout))
     return self.access
Beispiel #9
0
    def authenticate(self, password=False):
        # For some dumb reason it has to be a string
        if password:
            print("Log into Gfycat")
            username = input("Username: "******"Password: "******"grant_type": "password", "client_id": self.gfyid, "client_secret": self.gfysecret,
                    "username": username, "password": password}
        else:
            data = {"grant_type": "client_credentials", "client_id": self.gfyid,
                    "client_secret": self.gfysecret}

        url = "https://api.gfycat.com/v1/oauth/token"
        r = requests.post(url, data=str(data), headers={'User-Agent': consts.user_agent})
        try:
            response = r.json()
        except json.decoder.JSONDecodeError as e:
            print(r.text)
            raise
        self.timeout = int(time.time()) + response["expires_in"]
        self.access = response["access_token"]
        self.refresh = response["refresh_token"]
        CredentialsLoader.set_credential('gfycat', 'refresh_token', self.refresh)
        CredentialsLoader.set_credential('gfycat', 'access_token', self.access)
        CredentialsLoader.set_credential('gfycat', 'token_expiration', str(self.timeout))
Beispiel #10
0
    def authenticate(self, password=False):
        # For some dumb reason it has to be a string
        if password:
            print("Log into {}".format(self.SERVICE_NAME))
            username = input("Username: "******"Password: "******"grant_type": "password", "client_id": self.gfyid, "client_secret": self.gfysecret,
                    "username": username, "password": password}
        else:
            data = {"grant_type": "client_credentials", "client_id": self.gfyid,
                    "client_secret": self.gfysecret}

        url = self.TOKEN_URL
        r = requests.post(url, data=str(data), headers={'User-Agent': consts.user_agent})
        try:
            response = r.json()
        except json.decoder.JSONDecodeError as e:
            print(r.text)
            raise
        try:
            self.timeout = int(time.time()) + response["expires_in"]
            self.access = response["access_token"]
            self.refresh = response["refresh_token"]
        except KeyError:
            print(r.text)
            raise
        CredentialsLoader.set_credential(self.CREDENTIALS_BLOCK, 'refresh_token', self.refresh)
        CredentialsLoader.set_credential(self.CREDENTIALS_BLOCK, 'access_token', self.access)
        CredentialsLoader.set_credential(self.CREDENTIALS_BLOCK, 'token_expiration', str(self.timeout))
Beispiel #11
0
    def __init__(self, id, secret):
        self.client_id = id
        self.client_secret = secret
        self.auth = None
        self.mashape_key = None

        access = CredentialsLoader.get_credentials()['imgur'].get(
            'access_token', None)
        refresh = CredentialsLoader.get_credentials()['imgur'].get(
            'refresh_token', None)
        # imgur_credentials = self.loadimgur()
        if access and refresh:
            self.auth = AuthWrapper(access, refresh, id, secret)
        else:
            # Oauth setup
            print("Imgur Auth URL: ", self.get_auth_url('pin'))
            pin = input("Paste the pin here:")
            credentials = self.authorize(pin, 'pin')
            CredentialsLoader.set_credential('imgur', 'access_token',
                                             credentials['access_token'])
            CredentialsLoader.set_credential('imgur', 'refresh_token',
                                             credentials['refresh_token'])

            # self.saveimgur((credentials['access_token'], credentials['refresh_token']))

            self.set_user_auth(credentials['access_token'],
                               credentials['refresh_token'])
            self.auth = AuthWrapper(credentials['access_token'],
                                    credentials['refresh_token'], id, secret)
Beispiel #12
0
    def authenticate(self):
        print("Log into {}".format(self.SERVICE_NAME))
        print("Authorize here: " +
              self.OAUTH_BASE + self.AUTHORIZATION_URL + "?" + urllib.parse.urlencode({
                  "client_id": self.client_id, "response_type": "token", "state": "GifHostLibrary"
              }))
        result = input().strip()
        parts = urllib.parse.urlparse(result)
        params = urllib.parse.parse_qs(parts.fragment)
        print(parts, params)

        self.timeout = int(time.time()) + int(params["expires_in"][0])  # [0] quirk of parse_qs
        self.access = params["access_token"][0]
        self.refresh = params["refresh_token"][0]
        CredentialsLoader.set_credential(self.CREDENTIALS_BLOCK, 'refresh_token', self.refresh)
        CredentialsLoader.set_credential(self.CREDENTIALS_BLOCK, 'access_token', self.access)
        CredentialsLoader.set_credential(self.CREDENTIALS_BLOCK, 'token_expiration', str(self.timeout))
Beispiel #13
0
from core.credentials import CredentialsLoader

user_agent = "CoffinBot v{} by /u/nGoline"
spoof_user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0"
version = "0.0.1"
sleep_time = 90
username = CredentialsLoader.get_credentials()['reddit']['username']

bot_footer = "---\n\n^(I am a bot.) [^(Report an issue)]" \
                 "(https://www.reddit.com/message/compose/?to=nGoline&subject=CoffinBot%20Issue&message=" \
             "Add a link to the video or comment in your message%2C I'm not always sure which request is being " \
             "reported. Thanks for helping me out!)"

nsfw_reply_template = "##NSFW\n\nHere is your video!\n{}\n\n" + bot_footer

reply_template = "Here is your coffin meme!\n{}\n\n" + bot_footer

reply_ban_subject = "Here is your coffin meme!"

reply_ban_template = "Hi! Unfortunately, I am banned in that subreddit so I couldn't reply to your comment. " \
                       "I was still able to add the coffin meme to your video though!\n{}\n\n" + bot_footer

unnecessary_manual_message = "\n\nJust so you know, you don't have to manually give the video URL if it is in " \
                             "a parent comment or the post. I would have known what you meant anyways :)\n\n"

ignore_messages = [
    "Welcome to Moderating!", "re: Here is your coffin meme!",
    "Your reddit premium subscription has expired."
]

MP4 = 'mp4'
Beispiel #14
0
from core.credentials import CredentialsLoader

user_agent = "GifReversingBot v{} by /u/pmdevita"
spoof_user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0"
imgur_spoof_cookie = CredentialsLoader.get_credentials(
)['imgur']['imgur_cookie']
version = "2.6.9"
sleep_time = 90
username = CredentialsLoader.get_credentials()['reddit']['username']

bot_footer = "---\n\n^(I am a bot.) [^(Report an issue)]" \
                 "(https://www.reddit.com/message/compose/?to=pmdevita&subject=GifReversingBot%20Issue)"

nsfw_reply_template = "##NSFW\n\nHere is your gif!\n{}\n\n" + bot_footer

reply_template = "Here is your gif!\n{}\n\n" + bot_footer

reply_ban_subject = "Here is your gif!"

reply_ban_template = "Hi! Unfortunately, I am banned in that subreddit so I couldn't reply to your comment. " \
                       "I was still able to reverse your gif though!\n{}\n\n" + bot_footer

unnecessary_manual_message = "\n\nJust so you know, you don't have to manually give the gif URL if it is in " \
                             "a parent comment or the post. I would have known what you meant anyways :)\n\n"

ignore_messages = ["Welcome to Moderating!"]

VIDEO = 1
GIF = 2
OTHER = 3
LINK = 4
Beispiel #15
0
 def get(cls):
     if not cls.instance:
         credentials = CredentialsLoader.get_credentials()
         cls.instance = cls(credentials['imgur']['imgur_id'],
                            credentials['imgur']['imgur_secret'])
     return cls.instance
Beispiel #16
0
import unittest
import os
from tests.shims import praw
from datetime import datetime, timedelta

from core.credentials import CredentialsLoader
from pathlib import Path
# Configure config first
tests_folder = Path(os.path.dirname(os.path.realpath(__file__)))
credentials = CredentialsLoader().get_credentials(tests_folder / "configs/action.ini")

from core.reddit import ReplyObject
from core.action import increment_user_fixes
from core.history import SwitcharooLog


def reset_database(reddit, last_switcharoo: SwitcharooLog = None):
    if last_switcharoo:
        last_switcharoo.unbind()
    if os.path.exists(tests_folder / ".." / f"{credentials['database']['db_file']}"):
        os.remove(tests_folder / ".." / f"{credentials['database']['db_file']}")
    return SwitcharooLog(reddit)


def reddit_url_gen(subreddit, thread_id, comment_id=None, context=None):
    string = f"https://reddit.com/r/{subreddit}/comments/{thread_id}/_/"
    if comment_id:
        string += f"{comment_id}/"
    if context:
        string += f"?context={context}"
    return string
Beispiel #17
0
import unittest
import os
from tests.shims import praw
from datetime import datetime

from core.credentials import CredentialsLoader
from pathlib import Path
# Configure config first
tests_folder = Path(os.path.dirname(os.path.realpath(__file__)))
credentials = CredentialsLoader().get_credentials(
    Path(os.path.dirname(os.path.realpath(__file__))) / "configs/process.ini")

from core.process import check_errors
from core.history import SwitcharooLog
from core.issues import IssueTracker


def reset_database(reddit, last_switcharoo: SwitcharooLog = None):
    if last_switcharoo:
        last_switcharoo.unbind()
    if os.path.exists(tests_folder / ".." /
                      f"{credentials['database']['db_file']}"):
        os.remove(tests_folder / ".." /
                  f"{credentials['database']['db_file']}")
    return SwitcharooLog(reddit)


def reddit_url_gen(subreddit, thread_id, comment_id=None, context=None):
    string = f"https://reddit.com/r/{subreddit}/comments/{thread_id}/_/"
    if comment_id:
        string += f"{comment_id}/"
Beispiel #18
0
 def __init__(self):
     creds = CredentialsLoader.get_credentials()['streamable']
     self.email = creds['email']
     self.password = creds['password']
     self.headers = {'User-Agent': consts.user_agent}
Beispiel #19
0
from core.credentials import CredentialsLoader
from core.history import *
from core import constants as consts
from core.action import PrintAction, ModAction
from core.process import reprocess, check_errors, add_comment
credentials = CredentialsLoader.get_credentials()['reddit']

reddit = praw.Reddit(client_id=credentials["client_id"],
                     client_secret=credentials["client_secret"],
                     user_agent=consts.user_agent,
                     username=credentials["username"],
                     password=credentials["password"])

last_switcharoo = SwitcharooLog(reddit)

# Action object tracks switcharoo and performs a final action (delete/comment)
mode = CredentialsLoader.get_credentials()['general']['mode']

if mode == 'production':
    action = ModAction(reddit)
elif mode == 'development':
    action = PrintAction(reddit)


def roo_id_to_submission(id):
    roo = last_switcharoo.get_roo(id)
    print(f"https://reddit.com{roo.submission.permalink}")


def roo_id_to_comment(id):
    roo = last_switcharoo.get_roo(id)
Beispiel #20
0
import praw
import time
import traceback
import signal
import prawcore.exceptions
import os

from core.credentials import CredentialsLoader
reddit_creds = CredentialsLoader.get_credentials()['reddit']
credentials = CredentialsLoader.get_credentials()['general']

from core.process import process, reprocess
from core.history import SwitcharooLog
from core import constants as consts
from core.action import PrintAction, ModAction
from core.inbox import process_message, process_modmail


reddit = praw.Reddit(client_id=reddit_creds["client_id"],
                     client_secret=reddit_creds["client_secret"],
                     user_agent=consts.user_agent,
                     username=reddit_creds["username"],
                     password=reddit_creds["password"])

switcharoo = reddit.subreddit("switcharoo")


# Action object tracks switcharoo and performs a final action (delete/comment)
mode = credentials['mode']
operator = credentials['operator']
Beispiel #21
0
from pprint import pprint
import praw.exceptions
import prawcore.exceptions
from datetime import datetime, timedelta
from core import parse
from core.issues import IssueTracker
from core.strings import NewIssueDeleteStrings
from core.reddit import ReplyObject
from core.constants import ONLY_BAD, ONLY_IGNORED, ALL_ROOS
from core.history import SwitcharooLog, Switcharoo
from core.credentials import CredentialsLoader
from core.action import decide_subreddit_privated, increment_user_fixes
import core.operator

creds = CredentialsLoader.get_credentials()['general']
DRY_RUN = creds['dry_run'].lower() != "false"


def process(reddit, submission, last_switcharoo, action):
    # First, add this submission to the database
    tracker = IssueTracker()
    tracker.submission_processing = True
    roo = last_switcharoo.add(submission.id, link_post=not submission.is_self, user=submission.author.name,
                              roo_issues=tracker,
                              time=datetime.utcfromtimestamp(submission.created_utc))
    # Create an issue tracker made from it's errors
    tracker = check_errors(reddit, last_switcharoo, roo, init_db=True, submission=submission)

    # If it has issues, perform an action to correct it
    if tracker.has_issues():
        action.process(tracker, ReplyObject(submission), last_switcharoo.last_good(before_roo=roo, offset=0))
Beispiel #22
0
import praw


from core.credentials import get_credentials, CredentialsLoader
credentials = CredentialsLoader.get_credentials("../credentials.ini")['reddit']

from core.history import SwitcharooLog
from core import constants as consts

reddit = praw.Reddit(client_id=credentials["client_id"],
                     client_secret=credentials["client_secret"],
                     user_agent=consts.user_agent,
                     username=credentials["username"],
                     password=credentials["password"])

switcharoo = reddit.subreddit("switcharoo")

last_switcharoo = SwitcharooLog(reddit)

print("SwitcharooHelper Flair Sync v{} Ctrl+C to stop".format(consts.version))

for flair in switcharoo.flair():
    print(flair)
    current_count = last_switcharoo.stats.num_of_good_roos(user=flair['user'].name)
    badge_count = 0
    if flair['flair_css_class']:
        if flair['flair_css_class'][:6] == "badge-":
            badge_count = int(flair['flair_css_class'][6:])
        if badge_count > current_count:
            last_switcharoo.update_user_flair(flair['user'].name, badge_count - current_count)
# Bootstrap the main library into the path
import os
import sys
from pathlib import Path
tools_folder = Path(os.path.dirname(os.path.realpath(__file__)))
root = str(tools_folder.parent)
if root not in sys.path:
    sys.path.append(root)

from core.credentials import CredentialsLoader
credentials = CredentialsLoader.get_credentials(tools_folder / "../credentials.ini")['reddit']

import praw
import prawcore.exceptions
from core.history import SwitcharooLog
from core import constants as consts

reddit = praw.Reddit(client_id=credentials["client_id"],
                     client_secret=credentials["client_secret"],
                     user_agent=consts.user_agent,
                     username=credentials["username"],
                     password=credentials["password"])

switcharoo = reddit.subreddit("switcharoo")

last_switcharoo = SwitcharooLog(reddit)


def pad_zeros(number, zeros):
    string = str(number)
    return "".join(["0" for i in range(max(zeros - len(string), 0))]) + string
Beispiel #24
0
"""



class Gif(db.Entity):
    id = PrimaryKey(int, auto=True)
    origin_host = Required(int)
    origin_id = Required(str)
    reversed_host = Required(int)
    reversed_id = Required(str)
    time = Required(date)
    nsfw = Optional(bool)
    total_requests = Optional(int)
    last_requested_date = Optional(date)

creds = CredentialsLoader.get_credentials()['database']

if creds['type'] == 'sqlite':
    db.bind(provider='sqlite', filename='../database.sqlite', create_db=True)
elif creds['type'] == 'mysql':
    db.bind(provider="mysql", host=creds['host'], user=creds['username'], passwd=creds['password'],
            db=creds['database'])
else:
    raise Exception("No database configuration")

db.generate_mapping(create_tables=True)


def check_database(original_gif):
    # Have we reversed this gif before?
    with db_session:
Beispiel #25
0
import praw
import time
import pendulum

import prawcore.exceptions

from core.credentials import CredentialsLoader
from core import constants as consts
from core import parse

credentials = CredentialsLoader.get_credentials()['reddit']

reddit = praw.Reddit(client_id=credentials["client_id"],
                     client_secret=credentials["client_secret"],
                     user_agent=consts.user_agent.format(consts.version),
                     username=credentials["username"],
                     password=credentials["password"])

switcharoo = reddit.subreddit("switcharoo")

def get_newest_id(subreddit, index=0):
    """Retrieves the newest post's id. Used for starting the last switcharoo history trackers"""
    return [i for i in subreddit.new(params={"limit": "1"})][index].url

print("Paste the URL of the switcharoo comment you'd like to start at\nOr leave blank to start at the newest")
url = input()

if not url:
    url = get_newest_id(switcharoo, 1)

roo_count = 0
Beispiel #26
0
import requests
from requests_toolbelt import MultipartEncoder
import re
from io import BytesIO

from core.hosts import GifHost, Gif, GifFile
from core.credentials import CredentialsLoader
from core import constants as consts
from core.file import is_valid

catbox_hash = CredentialsLoader.get_credentials()['catbox']['hash']


class CatboxGif(Gif):
    process_id = True

    def _get_id(self, id, url):
        # Safety check
        if id:
            ext = id.split(".")[-1]
        else:
            ext = url.split(".")[-1]
            id = self.host.regex.findall(url)[0]
        if ext.lower() in [consts.MP4, consts.GIF, consts.WEBM]:
            # We should do file checks for safety because we could actually get some kind of nasty file
            return id
        return None

    def analyze(self):
        r = requests.get(self.url)
        file = BytesIO(r.content)
import prawcore
import time
import traceback
from core.credentials import CredentialsLoader
from core.regex import REPatterns
from core import constants as consts
from core.secret import secret_process
from core.context import extract_gif_from_comment

from pprint import pprint
import json
import datetime
from collections import defaultdict
from core.gif import GifHostManager

credentials = CredentialsLoader().get_credentials()

mode = credentials['general']['mode']
operator = credentials['general']['operator']

reddit = praw.Reddit(user_agent=consts.user_agent,
                     client_id=credentials['reddit']['client_id'],
                     client_secret=credentials['reddit']['client_secret'],
                     username=credentials['reddit']['username'],
                     password=credentials['reddit']['password'])


def get_date(seconds):
    return datetime.datetime.utcfromtimestamp(seconds).\
        replace(tzinfo=datetime.timezone.utc).astimezone(tz=None)
Beispiel #28
0
import requests
from io import BytesIO
from imgurpython.imgur.models.gallery_image import GalleryImage
from imgurpython.helpers.error import ImgurClientError

import core.hosts.imgur
from core import constants as consts
from core.gif import Gif
from core.regex import REPatterns
from core.hosts.imgur import ImgurClient
from core.hosts.gfycat import Gfycat as GfycatClient
from core.hosts.streamable import StreamableClient
from core.credentials import CredentialsLoader
from core.file import get_duration

creds = CredentialsLoader.get_credentials()
imgur = ImgurClient.get()
gfycat = GfycatClient.get()
streamable = StreamableClient.get()


class GifHost:
    type = None

    def __init__(self, context):
        self.context = context
        self.url = None

    def analyze(self):
        raise NotImplemented