# Path: cardinal/decorators.py
# def event(triggers):
#     if isinstance(triggers, str):
#         triggers = [triggers]
# 
#     if not isinstance(triggers, list):
#         raise TypeError("Event must be a trigger string or list of triggers")
# 
#     def wrap(f):
#         f.events = triggers
#         return f
# 
#     return wrap
# 
# Path: cardinal/exceptions.py
# class EventRejectedMessage(CardinalException):
#     """Raised when an event callback wants to reject an event."""


import re
import requests
import twitter
from urllib.parse import urlparse
from twisted.internet import defer
from twisted.internet.threads import deferToThread
from cardinal.decorators import event
from cardinal.exceptions import EventRejectedMessage
    def follow_short_link(self, url):
        r = yield deferToThread(requests.get,
                                url)

        # Twitter returns 400 in normal operation
        if not r.ok and r.status_code != 400:
            r.raise_for_status()

        return r.url

    @event('urls.detection')
    @defer.inlineCallbacks
    def handle_tweet(self, cardinal, channel, url):
        o = urlparse(url)

        # handle t.co short links
        if o.netloc == 't.co':
            url = yield self.follow_short_link(url)
            o = urlparse(url)

        if o.netloc in ('twitter.com', 'mobile.twitter.com') \
                and (match := re.match(r'^/.*/status/(\d+)$', o.path)):
            tweet_id = match.group(1)

            t = yield self.get_tweet(tweet_id)
            cardinal.sendMsg(channel, "Tweet from @{}: {}".format(
                t.user.screen_name,
                t.text,
            ))
        else:

# Path: cardinal/decorators.py
# def event(triggers):
#     if isinstance(triggers, str):
#         triggers = [triggers]
# 
#     if not isinstance(triggers, list):
#         raise TypeError("Event must be a trigger string or list of triggers")
# 
#     def wrap(f):
#         f.events = triggers
#         return f
# 
#     return wrap
# 
# Path: cardinal/exceptions.py
# class EventRejectedMessage(CardinalException):
#     """Raised when an event callback wants to reject an event."""


# Path: plugins/twitter/plugin.py
import re
import requests
import twitter
from urllib.parse import urlparse
from twisted.internet import defer
from twisted.internet.threads import deferToThread
from cardinal.decorators import event
from cardinal.exceptions import EventRejectedMessage

    def follow_short_link(self, url):
        r = yield deferToThread(requests.get,
                                url)

        # Twitter returns 400 in normal operation
        if not r.ok and r.status_code != 400:
            r.raise_for_status()

        return r.url

    @event('urls.detection')
    @defer.inlineCallbacks
    def handle_tweet(self, cardinal, channel, url):
        o = urlparse(url)

        # handle t.co short links
        if o.netloc == 't.co':
            url = yield self.follow_short_link(url)
            o = urlparse(url)

        if o.netloc in ('twitter.com', 'mobile.twitter.com') \
                and (match := re.match(r'^/.*/status/(\d+)$', o.path)):
            tweet_id = match.group(1)

            t = yield self.get_tweet(tweet_id)
            cardinal.sendMsg(channel, "Tweet from @{}: {}".format(
                t.user.screen_name,
                t.text,
            ))
        else:

            raise EventRejectedMessage