Esempio n. 1
0
class Worker():
    """
    This is a generic worker that will contain the source it will monitor.
    """
    def __init__(self, source: ISources):
        self.logger = Logger(__class__)
        self.source: ISources = source
        self.enabled: bool = False
        pass

    def check(self) -> bool:
        if len(self.source.links) >= 1:
            self.enabled = True
        else:
            self.enabled = False
            self.logger.info(
                f"{self.source.siteName} was not enabled.  Thread will exit.")

    def init(self) -> None:
        """
        This is the entry point for the worker.  
        Once its turned on it will check the Source for new items.
        """
        if self.source.sourceEnabled == True:
            self.logger.debug(f"{self.source.siteName} Worker has started.")

            while True:
                news = self.source.getArticles()

                # Check the DB if it has been posted
                for i in news:
                    exists = i.exists()

                    if exists == False:
                        i.add()

                        if len(self.source.hooks) >= 1:
                            dq = DiscordQueue()
                            dq.convert(i)
                            res = dq.add()

                            self.discordQueueMessage(i, res)

                self.logger.debug(
                    f"{self.source.siteName} Worker is going to sleep.")
                sleep(env.threadSleepTimer)

    def discordQueueMessage(self, i: Articles, added: bool) -> None:
        msg: str = ''
        if i.title != '':
            msg = i.title
        else:
            msg = i.description

        if added == True:
            self.logger.info(f"{msg} was added to the Discord queue.")
        else:
            self.logger.error(
                f"{msg} was not added to add to the Discord queue.")
Esempio n. 2
0
class Discord(IOutputs):
    def __init__(self) -> None:
        self.logger = Logger(__class__)
        self.table = DiscordQueue()
        self.tempMessage: DiscordWebhook = DiscordWebhook("placeholder")
        pass

    def enableThread(self) -> None:
        while True:
            # Tell the database to give us the queue on the table.
            try:
                queue = self.table.getQueue()

                for i in queue:

                    resp = self.sendMessage(i)

                    # Only remove the object from the queue if we sent it out correctly.
                    safeToRemove: bool = True
                    for r in resp:
                        if r.status_code != 204:
                            safeToRemove = False

                    if safeToRemove == True:
                        i.remove()

                    sleep(env.discord_delay_seconds)
            except Exception as e:
                self.logger.error(
                    f"Failed to post a message. {i.title}. Status_code: {resp.status_code}. msg: {resp.text}. error {e}"
                )

            sleep(env.discord_delay_seconds)

    def buildMessage(self, article: DiscordQueue) -> None:
        # reset the stored message
        self.tempMessage = DiscordWebhook("placeholder")

        # Extract the webhooks that relate to this site
        webhooks: List[str] = self.getHooks(article.siteName)

        # Make a new webhook with the hooks that relate to this site
        hook: DiscordWebhook = DiscordWebhook(webhooks)
        # hook.content = article.link

        title = article.title
        if len(title) >= 128:
            title = f"{title[0:128]}..."

        # Make a new Embed object
        embed: DiscordEmbed = DiscordEmbed(title=title)  # , url=article.link)

        try:
            authorIcon = self.getAuthorIcon(article.authorImage,
                                            article.siteName)
            embed.set_author(name=article.authorName,
                             url=None,
                             icon_url=authorIcon)
        except:
            pass

        # Discord Embed Description can only contain 2048 characters
        ch = ConvertHtml()
        if article.description != "":
            description: str = str(article.description)
            description = self.convertFromHtml(description)
            description = ch.replaceImages(description, '')
            #description = self.replaceImages(description)
            descriptionCount = len(description)
            if descriptionCount >= 2048:
                description = description[0:2040]
                description = f"{description}..."
            embed.description = description

        # Figure out if we have video based content
        if article.video != "":
            embed.description = "View the video online!"
            embed.set_video(url=article.video,
                            height=article.videoHeight,
                            width=article.videoWidth)

        try:
            if article.thumbnail != "":
                if " " in article.thumbnail:
                    s = article.thumbnail.split(" ")
                    embed.set_image(url=s[0])
                else:
                    embed.set_image(url=article.thumbnail)
        except Exception as e:
            self.logger.warning(
                f"Failed to attach a thumbnail. \r\n {e}\r\n thumbnails: {article.thumbnail}"
            )

        # add the link to the embed
        embed.add_embed_field(name="Link:", value=article.link)

        # Build our footer message
        footer = self.buildFooter(article.siteName)
        footerIcon = self.getFooterIcon(article.siteName)
        embed.set_footer(icon_url=footerIcon, text=footer)

        embed.set_color(color=self.getEmbedColor(article.siteName))

        hook.add_embed(embed)
        self.tempMessage = hook

    def sendMessage(self, article: DiscordQueue) -> List[Response]:
        if article.title != "":
            self.logger.info(f"Discord - Sending article '{article.title}'")
        else:
            self.logger.info(
                f"Discord - Sending article '{article.description}'")
        self.buildMessage(article)
        try:
            res = self.tempMessage.execute()
        except Exception as e:
            self.logger.critical(
                f"Failed to send to Discord.  Check to ensure the webhook is correct. Error: {e}"
            )

        hooks: int = len(self.getHooks(article.siteName))

        # Chcekcing to see if we returned a single responce or multiple.
        if hooks == 1:
            responces = list()
            responces.append(res)
        else:
            responces = res

        return responces

    def getHooks(self, newsSource: str) -> List[str]:
        hooks = list()
        try:
            dwh = DiscordWebHooks(name=newsSource).findAllByName()
            for i in dwh:
                hooks.append(i.key)
            return hooks
        except Exception as e:
            self.logger.critical(
                f"Unable to find DiscordWebhook for {newsSource.siteName}")

    def convertFromHtml(self, msg: str) -> str:
        msg = msg.replace("<h2>", "**")
        msg = msg.replace("</h2>", "**")
        msg = msg.replace("<h3>", "**")
        msg = msg.replace("</h3>", "**\r\n")
        msg = msg.replace("<strong>", "**")
        msg = msg.replace("</strong>", "**\r\n")
        msg = msg.replace("<ul>", "\r\n")
        msg = msg.replace("</ul>", "")
        msg = msg.replace("</li>", "\r\n")
        msg = msg.replace("<li>", "> ")
        msg = msg.replace("&#8220;", '"')
        msg = msg.replace("&#8221;", '"')
        msg = msg.replace("&#8230;", "...")
        msg = msg.replace("<b>", "**")
        msg = msg.replace("</b>", "**")
        msg = msg.replace("<br>", "\r\n")
        msg = msg.replace("<br/>", "\r\n")
        msg = msg.replace("\xe2\x96\xa0", "*")
        msg = msg.replace("\xa0", "\r\n")
        msg = msg.replace("<p>", "")
        msg = msg.replace("</p>", "\r\n")

        msg = self.replaceLinks(msg)
        return msg

    def replaceLinks(self, msg: str) -> str:
        """
        Find the HTML links and replace them with something discord supports.
        """
        # links = re.findall("(?<=<a )(.*)(?=</a>)", msg)
        msg = msg.replace("'", '"')
        links = re.findall("<a(.*?)a>", msg)
        for l in links:
            hrefs = re.findall('href="(.*?)"', l)
            texts = re.findall(">(.*?)</", l)
            if len(hrefs) >= 1 and len(texts) >= 1:
                discordLink = f"[{texts[0]}]({hrefs[0]})"
                msg = msg.replace(f"<a{l}a>", discordLink)
        return msg

    def replaceImages(self, msg: str) -> str:
        imgs = re.findall("<img (.*?)>", msg)
        for i in imgs:
            # Removing the images for now.
            # src = re.findall('src=(.*?)">', i)
            replace = f"<img {i}>"
            msg = msg.replace(replace, "")
        return msg

    def getAuthorIcon(self, authorIcon: str, siteName: str) -> str:
        if authorIcon != "":
            return authorIcon
        else:
            if (siteName == "Final Fantasy XIV"
                    or siteName == "Phantasy Star Online 2"
                    or siteName == "Pokemon Go Hub"):
                res = Icons(site=f"Default {siteName}").findAllByName()
                return res[0].filename
            else:
                s: List[str] = siteName.split(" ")
                if s[0] == "RSS":
                    # res = Icons(site=f"Default {s[1]}").findAllByName()
                    res = Icons(site=siteName).findAllByName()
                else:
                    res = Icons(site=f"Default {s[0]}").findAllByName()
                return res[0].filename

    def buildFooter(self, siteName: str) -> str:
        footer = ""
        end: str = "Brought to you by NewsBot"
        if "reddit" in siteName.lower():
            s = siteName.split(" ")
            footer = f"{end}"
        elif "Youtube" in siteName:
            s = siteName.split(" ")
            footer = f"{s[1]} - {end}"
        elif "Instagram" in siteName or "Twitter" in siteName:
            s = siteName.split(" ")
            if s[1] == "tag":
                footer = f"#{s[2]} - {end}"
            elif s[1] == "user":
                footer = f"{s[2]} - {end}"
        elif "RSS" in siteName:
            s = siteName.split(" ")
            footer = f"{s[1]} - {end}"
        else:
            footer = end

        return footer

    def getFooterIcon(self, siteName: str) -> str:
        if (siteName == "Phatnasy Star Online 2"
                or siteName == "Pokemon Go Hub"
                or siteName == "Final Fantasy XIV"):
            res = Icons(site=f"Default {siteName}").findAllByName()
            return res[0].filename
        else:
            s: List[str] = siteName.split(" ")
            values = (f"Default {s[1]}", f"Default {s[0]}", siteName)
            for v in values:
                r = Icons(site=v).findAllByName()
                if len(r) == 1:
                    res = r
            # if s[0].lower() == 'rss':
            #    res = Icons(site=f"Default {s[1]}").findAllByName()
            # else:
            #    res = Icons(site=f"Default {s[0]}").findAllByName()

            try:
                if res[0].filename != "":
                    return res[0].filename
                else:
                    return ""
            except:
                return ""

    def getEmbedColor(self, siteName: str) -> int:
        # Decimal values can be collected from https://www.spycolor.com
        if "Reddit" in siteName:
            return 16395272
        elif "YouTube" in siteName:
            return 16449542
        elif "Instagram" in siteName:
            return 13303930
        elif "Twitter" in siteName:
            return 1937134
        elif "Final Fantasy XIV" in siteName:
            return 11809847
        elif "Pokemon Go Hub" in siteName:
            return 2081673
        elif "Phantasy Star Online 2" in siteName:
            return 5557497
        elif "Twitch" in siteName:
            return 9718783
        else:
            return 0