Exemple #1
0
def plugin(srv, item):

    _DEFAULT_URL = 'https://mastodon.social'

    srv.logging.debug("*** MODULE=%s: service=%s, target=%s", __file__, item.service, item.target)

    if not HAVE_MASTODON:
        srv.logging.error("Python Mastodon.py is not installed")
        return False

    # item.config is brought in from the configuration file
    config   = item.config

    clientcreds, usercreds, base_url = item.addrs

    text = item.message

    try:
        mastodon = Mastodon(
            client_id = clientcreds,
            access_token = usercreds,
            api_base_url = base_url
        )

        mastodon.toot(text)
    except Exception, e:
        srv.logging.warning("Cannot post to Mastodon: %s" % (str(e)))
        return False
Exemple #2
0
    def register(self):
        import webbrowser

        self.check_params('handle')
        api_base_url = self.params['handle'].split('@')[-1]
        # get client data
        client_id = self.context.params.get(service_name, 'client_id')
        client_secret = self.context.params.get(service_name, 'client_secret')
        if (not client_id) or (not client_secret):
            client_id, client_secret = Mastodon.create_app(
                'pywws', scopes=['write'], api_base_url=api_base_url)
            self.context.params.set(service_name, 'client_id', client_id)
            self.context.params.set(service_name, 'client_secret', client_secret)
        # create api
        api = Mastodon(client_id=client_id, client_secret=client_secret,
                       api_base_url=api_base_url)
        # authorise
        auth_request_url = api.auth_request_url(scopes=['write'])
        if not webbrowser.open(auth_request_url, new=2, autoraise=0):
            print('Please use a web browser to open the following URL')
            print(auth_request_url)
        if sys.version_info[0] >= 3:
            input_ = input
        else:
            input_ = raw_input
        code = input_('Please enter the auth code shown in your web browser: ')
        code = code.strip()
        # log in
        access_token = api.log_in(code=code, scopes=['write'])
        self.context.params.set(service_name, 'access_token', access_token)
Exemple #3
0
    def auth(self, request):
        """
            get the auth of the services
            :param request: contains the current session
            :type request: dict
            :rtype: dict
        """
        # create app
        redirect_uris = '%s://%s%s' % (request.scheme, request.get_host(),
                                       reverse('mastodon_callback'))
        us = UserService.objects.get(user=request.user,
                                     name='ServiceMastodon')
        client_id, client_secret = MastodonAPI.create_app(
            client_name="TriggerHappy", api_base_url=us.host,
            redirect_uris=redirect_uris)

        us.client_id = client_id
        us.client_secret = client_secret
        us.save()

        us = UserService.objects.get(user=request.user,
                                     name='ServiceMastodon')
        # get the token by logging in
        mastodon = MastodonAPI(
            client_id=client_id,
            client_secret=client_secret,
            api_base_url=us.host
        )
        token = mastodon.log_in(username=us.username, password=us.password)
        us.token = token
        us.save()
        return self.callback_url(request)
Exemple #4
0
 def callback_url(self, request):
     us = UserService.objects.get(user=request.user,
                                  name='ServiceMastodon')
     mastodon = MastodonAPI(
         client_id=us.client_id,
         client_secret=us.client_secret,
         access_token=us.token,
         api_base_url=us.host
     )
     redirect_uris = '%s://%s%s' % (request.scheme, request.get_host(),
                                    reverse('mastodon_callback'))
     return mastodon.auth_request_url(redirect_uris=redirect_uris)
Exemple #5
0
    def save_data(self, trigger_id, **data):
        """
            let's save the data

            :param trigger_id: trigger ID from which to save data
            :param data: the data to check to be used and save
            :type trigger_id: int
            :type data:  dict
            :return: the status of the save statement
            :rtype: boolean
        """
        title, content = super(ServiceMastodon, self).save_data(trigger_id, **data)
        # check if we have a 'good' title
        if self.title_or_content(title):
            content = str("{title} {link}").format(title=title, link=data.get('link'))
            content += get_tags(Mastodon, trigger_id)
        # if not then use the content
        else:
            content += " " + data.get('link') + " " + get_tags(Mastodon, trigger_id)
        content = self.set_mastodon_content(content)

        us = UserService.objects.get(user=self.user, token=self.token, name='ServiceMastodon')
        try:
            toot_api = MastodonAPI(client_id=us.client_id, client_secret=us.client_secret, access_token=self.token,
                                   api_base_url=us.host)
        except ValueError as e:
            logger.error(e)
            status = False
            update_result(trigger_id, msg=e, status=status)

        media_ids = None
        try:
            if settings.DJANGO_TH['sharing_media']:
                # do we have a media in the content ?
                content, media = self.media_in_content(content)
                if media:
                    # upload the media first
                    media_ids = toot_api.media_post(media_file=media)
                    media_ids = [media_ids]
            toot_api.status_post(content, media_ids=media_ids)

            status = True
        except Exception as inst:
            logger.critical("Mastodon ERR {}".format(inst))
            status = False
            update_result(trigger_id, msg=inst, status=status)
        return status
def get_service(hass, config, discovery_info=None):
    """Get the Mastodon notification service."""
    from mastodon import Mastodon
    from mastodon.Mastodon import MastodonUnauthorizedError

    client_id = config.get(CONF_CLIENT_ID)
    client_secret = config.get(CONF_CLIENT_SECRET)
    access_token = config.get(CONF_ACCESS_TOKEN)
    base_url = config.get(CONF_BASE_URL)

    try:
        mastodon = Mastodon(
            client_id=client_id, client_secret=client_secret,
            access_token=access_token, api_base_url=base_url)
        mastodon.account_verify_credentials()
    except MastodonUnauthorizedError:
        _LOGGER.warning("Authentication failed")
        return None

    return MastodonNotificationService(mastodon)
Exemple #7
0
def get_credential(**config):
    api_base_url = config.get('instance_url')
    if not api_base_url:
        raise Exception(
            'Correctly specify instance_url on {}.'.format(CONFIG_FILE_NAME))
    if not exists(CLIENT_SECRET_FILE_NAME):
        Mastodon.create_app(
            client_name=config.get('app_name') or 'chatbotdon',
            website=config.get('website')
            or 'https://github.com/kakakaya/chatbotdon',
            to_file=CLIENT_SECRET_FILE_NAME,
            api_base_url=api_base_url,
        )

    access_token = None
    if exists(USER_SECRET_FILE_NAME):
        access_token = USER_SECRET_FILE_NAME
    m = Mastodon(client_id=CLIENT_SECRET_FILE_NAME,
                 access_token=access_token,
                 api_base_url=api_base_url)
    if not access_token:
        m.log_in(
            username=config.get('username'),
            password=config.get('password'),
            to_file=USER_SECRET_FILE_NAME,
        )
    return m
Exemple #8
0
def get_access_token():
    config = ConfigParser()
    config.readfp(io.open('token.ini', 'r', encoding='utf_8_sig'))

    section = 'Token'
    api_base_url = config.get(section, 'api_base_url')
    username = config.get(section, 'username')
    password = config.get(section, 'password')

    client_id = config.get(section, 'client_id')
    access_token = config.get(section, 'access_token')
    scopes = ['read', 'write']

    Mastodon.create_app(
        'TOMOYODon',
        scopes=scopes,
        api_base_url=api_base_url,
        to_file=client_id
    )

    mastodon = Mastodon(
        client_id=client_id,
        api_base_url=api_base_url
    )

    mastodon.log_in(
        username=username,
        password=password,
        scopes=scopes,
        to_file=access_token
    )
Exemple #9
0
def login():
    response.headers['Access-Control-Allow-Origin'] = '*'
    response.content_type = "application/json"
    username = request.forms.get("username")  # pylint: disable=no-member
    password = request.forms.get("password")  # pylint: disable=no-member
    instance = request.forms.get("instance")  # pylint: disable=no-member
    if debug:
        appname = "Nordcast (debug)"
    else:
        appname = "Nordcast"
    if not os.path.exists('clientcred.' + instance + '.secret'):
        Mastodon.create_app(appname,
                            api_base_url='https://' + instance,
                            to_file='clientcred.' + instance + '.secret')
    mastodon = Mastodon(client_id='clientcred.' + instance + '.secret',
                        api_base_url='https://' + instance)
    mastodon.log_in(
        username,
        password,
        to_file='authtokens/' + username + '.' + instance + '.secret',
    )
    if not os.path.exists("usercred.secret"):
        suid = str(uuid.uuid1())
        if r.get("nordcast/uuids/" + username + "$$" + instance) == None:
            r.set("nordcast/uuids/" + username + "$$" + instance, suid)
        else:
            r.set(
                "nordcast/uuids/" + username + "$$" + instance,
                str(r.get("nordcast/uuids/" + username + "$$" +
                          instance)).replace("b'", "").replace("'", "") + "," +
                suid)
        return json.dumps({"login": "******", "uuid": suid})
    else:
        return "{\"login\": \"error\"}"
Exemple #10
0
def initialize_mastodon():
    server_url = config("MASTODON_SERVER")
    # make data dir
    mastodon_data_path = Path(__file__).parents[2] / "data" / "mastodon"
    # フォルダの方は Error を回避
    try:
        Path(mastodon_data_path.parent).mkdir()
        Path(mastodon_data_path).mkdir()
    except FileExistsError:
        print("you have already make mastodon data folder")
    mastodon_client_data_path = mastodon_data_path / "my_clientcred.txt"
    mastodon_user_data_path = mastodon_data_path / "my_usercred.txt"
    # If you already have data file, Raise error
    for path in (mastodon_user_data_path, mastodon_client_data_path):
        if path.exists():
            print(path.as_posix(), "is already exists")
            return False
    # get mastodon data
    Mastodon.create_app("client name",
                        api_base_url=server_url,
                        to_file=mastodon_client_data_path.as_posix())
    mastodon = Mastodon(client_id=mastodon_client_data_path.as_posix(),
                        api_base_url=server_url)
    mastodon.log_in(config("MASTODON_EMAIL"),
                    config("MASTODON_PASS"),
                    to_file=mastodon_user_data_path.as_posix())
    return True
Exemple #11
0
class HmsMastodon:
    """A class that provides features around the Mastodon wrapper."""
    def __init__(self):
        """Default constructor."""
        self.mastodon = Mastodon(settings.CLIENT_CREDS_FILE,
                                 access_token=settings.USER_CREDS_FILE,
                                 api_base_url=settings.API_BASE_URL)
        self.listener = None

    def init_streaming(self, rabbit):
        """Initialize the Mastodon streaming API."""
        self.listener = TootStreamListener(rabbit)
        self.mastodon.user_stream(self.listener)

    def toot_new_status(self, data):
        """Toots the new status of the space."""
        if data['is_open']:
            short_msg = strings.TWAUM_OPEN_SHORT
            long_msg = strings.TWAUM_OPEN
        else:
            short_msg = strings.TWAUM_CLOSED_SHORT
            long_msg = strings.TWAUM_CLOSED

        get_logger().info("Sending new status toot: {}".format(short_msg))
        self.mastodon.status_post(long_msg, spoiler_text=short_msg)

    def toot(self, message):
        """Send a toot."""
        get_logger().info("Sending toot: {}".format(message))
        self.mastodon.status_post(message)
Exemple #12
0
def main(config, data_dir, callback, exclude_user, timeline, limit, account_id):
    def online_callback(
            client=None,
            account=None,
            media_url=None,
            notif_type=None,
            orig_status=None,
            status=None,
            cache=None,
            ):
        """Take a toot from an online source, and archive it."""

        if not status:
            return

        try:
            session = sessionmaker(get_engine(data_dir))()
            session.add(toot_from_status(status))
            session.commit()

            print('    ... Saved archived message!')

        except Exception as e:
            print(str(e))

    cfg = json.load(open(config))

    try:
        client = Mastodon(
            access_token=cfg["mastodon_token"],
            api_base_url=cfg["mastodon_instance"],
        )

        kwargs = {
            'client': client,
            'config': config,
            'data_dir': data_dir,
            'exclude_user': exclude_user,
        }

        if callback:  # Callback for archived streamer was provided
            ArchiveSource(callback=callback, **kwargs).process()  # TODO: Accept query params

        else:  # Otherwise we're just a message sink
            streamer = ArchiveSink(callback=online_callback, **kwargs)

            streamer.process(timeline=timeline, limit=limit, account_id=account_id)

    except Exception as e:
        print(str(e))

        time.sleep(10)
Exemple #13
0
def kafka_toot(id, password, mastodon_url, kafka_url, kafka_topic):
    if id != None and password != None and mastodon_url != None and kafka_url != None and kafka_topic != None:
        consumer = KafkaConsumer(kafka_topic, bootstrap_servers=kafka_url)

        login(id, password, mastodon_url)
        mastodon = Mastodon(access_token='pytooter_usercred.secret',
                            api_base_url=mastodon_url)

        for msg in consumer:
            print(msg.value)
            mastodon.toot(msg.value)

    if id == None:
        print('ERROR: ID env is not defined.')
    if password == None:
        print('ERROR: PASSWORD env is not defined.')
    if mastodon_url == None:
        print('ERROR: MASTODON_URL env is not defined.')
    if kafka_url == None:
        print('ERROR: KAFKA_URL env is not defined.')
    if kafka_topic == None:
        print('ERROR: KAFKA_TOPIC env is not defined.')
Exemple #14
0
def init(config):
    API_BASE_URL = config['Mastodon']['API_BASE_URL']

    if not os.path.exists('clientcred.secret'):
        Mastodon.create_app(
            config['Mastodon']['APP_NAME'],
            api_base_url = API_BASE_URL,
            to_file = 'clientcred.secret'
        )

    mastodon = Mastodon(
        client_id = 'clientcred.secret',
        api_base_url = API_BASE_URL
    )

    mastodon.log_in(
        config['Mastodon']['EMAIL'],
        config['Mastodon']['PASS_WORD'],
        to_file = 'usercred.secret'
    )

    return mastodon
Exemple #15
0
def get_mastodon(request):
    if not (request.session.has_key('instance') and
            (request.session.has_key('username')
             or request.session.has_key('access_token'))):
        raise NotLoggedInException()

    pool = MastodonPool()
    if request.session.has_key('access_token'):
        try:
            client = Client.objects.get(
                api_base_id=request.session['instance'])
        except (Client.DoesNotExist, Client.MultipleObjectsReturned):
            raise NotLoggedInException()
        if request.session['access_token'] in pool.keys():
            mastodon = pool[request.session['access_token']]
        else:
            mastodon = Mastodon(client_id=client.client_id,
                                client_secret=client.client_secret,
                                api_base_url=client.api_base_id,
                                access_token=request.session['access_token'],
                                ratelimit_method='throw')
            pool[request.session['access_token']] = mastodon
    else:
        try:
            client = Client.objects.get(
                api_base_id=request.session['instance'])
            user = Account.objects.get(username=request.session['username'])
        except (Client.DoesNotExist, Client.MultipleObjectsReturned,
                Account.DoesNotExist, Account.MultipleObjectsReturned):
            raise NotLoggedInException()
        if user.access_token in pool.keys():
            mastodon = pool[user.access_token]
        else:
            mastodon = Mastodon(client_id=client.client_id,
                                client_secret=client.client_secret,
                                access_token=user.access_token,
                                api_base_url=client.api_base_id,
                                ratelimit_method="throw")
    return mastodon
Exemple #16
0
def create_application(url):
    print("Application not created, creating now...")

    if Mastodon.create_app("Mastopy",
                           to_file="Mastopy_client-cred.secret",
                           scopes=['read', 'write'],
                           api_base_url=url):
        print("Application created!")
    else:
        print("Failed to create application!")
        sys.exit(1)

    pass
Exemple #17
0
def get_mastodon(config_file):
    config = common.get_json(config_file)
    if config == None:
        print("File %s not found, exiting." % filename, file=sys.stderr)
        sys.exit(1)
    mastodon = Mastodon(
        client_id=config["client_id"],
        client_secret=config["client_secret"],
        access_token=config["access_token"],
        api_base_url='https://' +
        config["mastodon_hostname"]  # E.g., mastodon.social
    )
    return mastodon
Exemple #18
0
 def start_register(self, server):
     try:
         (id,secret)=Mastodon.create_app(
                 client_name='mastaj xmpp gateway',
                 api_base_url="https://"+server
             )
         m=Mastodon(
             client_id=id,
             client_secret=secret,
             api_base_url="https://"+server
         )
         url=m.auth_request_url(
             client_id=id,
             scopes=['read','write','follow'],
             redirect_uris='urn:ietf:wg:oauth:2.0:oob'
         )
         self.mastodon=m
         self.mastodon_id=server
         print(url)
         return url
     except MastodonNetworkError:
         raise NetworkError()
Exemple #19
0
    def login(self, pace=False):
        """
        Register app, authorize and return an instance of ``Mastodon``
        """
        url = self.url
        client_secret = self.client_secret
        user_secret = self.user_secret
        user_secret2 = self.user_secret2

        if not os.path.isfile(client_secret):
            self.register()

        if not os.path.isfile(user_secret) and os.path.isfile(user_secret2):
            user_secret = user_secret2

        if not os.path.isfile(user_secret):
            mastodon = self.authorize()

        else:

            if pace:

                # in case the user kept running into a General API problem
                mastodon = Mastodon(client_id=client_secret,
                                    access_token=user_secret,
                                    api_base_url=url,
                                    ratelimit_method="pace",
                                    ratelimit_pacefactor=0.9,
                                    request_timeout=300)

            else:

                # the defaults are ratelimit_method='wait',
                # ratelimit_pacefactor=1.1, request_timeout=300
                mastodon = Mastodon(client_id=client_secret,
                                    access_token=user_secret,
                                    api_base_url=url)

        return mastodon
Exemple #20
0
def toot(bot, trigger):
    #bot.say(os.getcwd())
    mytoot = trigger.split(' ', 1)[1]

    try:
        keys = open(os.getcwd() + "/SECRET_SAUCE/masto.txt", "r")

        client_id = keys.readline().rstrip()
        client_secret = keys.readline().rstrip()
        access_token = keys.readline().rstrip()
        api_base_url = keys.readline().rstrip()

        mastodon = Mastodon(client_id, client_secret, access_token,
                            api_base_url)

        mastodon.toot(mytoot)

        keys.close()

        bot.say("OK, I tooted that to " + api_base_url)
    except:
        bot.say("It all went to shit, son.")
Exemple #21
0
def connect(type='twitter'
            ):  # connect to twitter API using keys from developer account
    if type == 'twitter':
        return twitter.Api(consumer_key=MY_CONSUMER_KEY,
                           consumer_secret=MY_CONSUMER_SECRET,
                           access_token_key=MY_ACCESS_TOKEN_KEY,
                           access_token_secret=MY_ACCESS_TOKEN_SECRET,
                           tweet_mode='extended')
    elif type == 'mastodon':
        return Mastodon(client_id=CLIENT_CRED_FILENAME,
                        api_base_url=MASTODON_API_BASE_URL,
                        access_token=USER_ACCESS_FILENAME)
    return None
Exemple #22
0
def login_mastodon(user):
    """
    Mastodon OAuth authentication process.
    :return: redirect to city page.
    """
    # get app tokens
    instance_url = request.forms.get('instance_url')
    masto_email = request.forms.get('email')
    masto_pass = request.forms.get('pass')
    client_id, client_secret = user.get_mastodon_app_keys(instance_url)
    m = Mastodon(client_id=client_id,
                 client_secret=client_secret,
                 api_base_url=instance_url)
    try:
        access_token = m.log_in(masto_email, masto_pass)
        user.save_masto_token(access_token, instance_url)
        return city_page(
            user.get_city(),
            info='Thanks for supporting decentralized social networks!')
    except Exception:
        logger.error('Login to Mastodon failed.', exc_info=True)
        return dict(error='Login to Mastodon failed.')
Exemple #23
0
    def check(self, request, user):
        """
        check if the service is well configured
        :return: Boolean
        """
        redirect_uris = '%s://%s%s' % (request.scheme, request.get_host(),
                                       reverse('mastodon_callback'))
        us = UserService.objects.get(user=user, name='ServiceMastodon')
        client_id, client_secret = MastodonAPI.create_app(
            client_name="TriggerHappy",
            api_base_url=us.host,
            redirect_uris=redirect_uris)

        # get the token by logging in
        mastodon = MastodonAPI(client_id=client_id,
                               client_secret=client_secret,
                               api_base_url=us.host)
        try:
            mastodon.log_in(username=us.username, password=us.password)
            return True
        except MastodonIllegalArgumentError as e:
            return e
    def initalize(self):
        if not os.path.isfile("security_bot_clientcred.txt"):
            print("Creating app")
            self.mastodon = Mastodon.create_app(
                'security_bot_main',
                to_file='security_bot_clientcred.txt',
                api_base_url=self.instance_url)

        # Fetch access token if I didn't already
        if not os.path.isfile("security_bot_usercred.txt"):
            print("Logging in")
            self.mastodon = Mastodon(client_id='security_bot_clientcred.txt',
                                     api_base_url=self.instance_url)
            email = sys.argv[1]
            password = sys.argv[2]
            self.mastodon.log_in(email,
                                 password,
                                 to_file='security_bot_usercred.txt')

        self.mastodon = Mastodon(client_id='security_bot_clientcred.txt',
                                 access_token='security_bot_usercred.txt',
                                 api_base_url=self.instance_url)
Exemple #25
0
def do_post_mastodon(tweet_text, image_file, image_mime_type):
    if not DO_MASTODON:
        return ''
    api = Mastodon(
        client_secret=CLIENT_SECRET,
        access_token=ACCESS_TOKEN,
        api_base_url='https://mastodon.cloud'
    )
    with open(image_file, 'rb') as f:
        content = f.read(-1)
    media_id = 0
    post_result = api.media_post(
        media_file=content,
        mime_type=image_mime_type,
        description=tweet_text)
    media_id = int(post_result['id'])

    if media_id != 0:
        status = api.status_post(status=tweet_text, media_ids=media_id)
    else:
        status = api.status_post(status=tweet_text)
    return "Tooted: \"{0}\"\n".format(tweet_text)
Exemple #26
0
 def __init__(self,
              logger: Logger,
              client_id: str,
              client_secret: str,
              access_token: str,
              api_base_url: str,
              reply_everyone: bool = False):
     self.logger = logger
     self.api = MastodonAPI(client_id, client_secret, access_token,
                            api_base_url)
     self.myself = self.api.account_verify_credentials()
     self.reply_everyone = reply_everyone
     self._cached = dict()
    def __init__(
        self,
        mastodon: Mastodon,
        hihobot: Hihobot,
        wait_span: float,
        myname_list: List[str],
    ):
        self.mastodon = mastodon
        self.hihobot = hihobot
        self.wait_span = wait_span
        self.myname_list = myname_list

        self.me = mastodon.account_verify_credentials()
Exemple #28
0
def get_mastodon_configs() -> dict:
    instance_url = input("Mastodon instance url: ")
    (client_id, client_secret) = Mastodon.create_app(
            'diadon',
            api_base_url=instance_url,
            scopes=['read', 'write'],
            website='https://github.com/f-person/diadon')
    api = Mastodon(client_id,
                   client_secret,
                   api_base_url=instance_url)

    email = input("Email: ")
    password = getpass()

    access_token = api.log_in(email, password, scopes=['read', 'write'])

    return {
        'instance_url': instance_url,
        'client_id': client_id,
        'client_secret': client_secret,
        'access_token': access_token
    }
Exemple #29
0
def createmastodonapp(appname, baseurl, email, password):
    Mastodon.create_app(appname,
                        api_base_url=baseurl,
                        to_file='pytooter_clientcred.secret')
    # Then login. This can be done every time, or use persisted.
    mastodon = Mastodon(client_id='pytooter_clientcred.secret',
                        api_base_url=baseurl)
    mastodon.log_in(email, password, to_file='pytooter_usercred.secret')
Exemple #30
0
def crosspost():
    # Set up Mastodon connection
    mastodon = Mastodon(
        client_id = 'pytooter_clientcred.secret',
        api_base_url = 'https://lgbt.io'
    )

    mastodon.log_in(
        macreds.username,
        macreds.password,
        to_file = 'pytooter_usercred.secret'
    )

    mastodon = Mastodon(
        client_id = 'pytooter_clientcred.secret',
        access_token = 'pytooter_usercred.secret',
        api_base_url = macreds.base_url
    )

    # Set up Twitter connection
    auth = tweepy.OAuthHandler(twcreds.consumer_key, twcreds.consumer_secret)
    auth.set_access_token(twcreds.access_token, twcreds.access_token_secret)
    api = tweepy.API(auth)

    # Set up files
    idfile = "twcache.txt"
    idfromfile = open(idfile, 'r')
    lastid = idfromfile.read()
    #print("Last status ID read from file: " + laststatus)

    # Get tweets since last status recorded in idfile
    try:
        status = api.user_timeline(since_id=lastid, count=20, )
    except:
        status = api.user_timeline(count=20)

    # Loop through tweets
    for i in reversed(status):
        # Toot
        if (i.in_reply_to_user_id == 53925971 or not i.in_reply_to_user_id) and i.is_quote_status == False and i.retweeted == False:
            mastodon.toot(i.text)
            #print(i.text)
        
        # Update idfile with ID of last successfully tooted tweet
        lastid = i.id
        idtofile = open(idfile, 'w')
        idtofile.write(str(lastid))
        idtofile.close()
Exemple #31
0
def init_twitter_api():
    global api
    if (os.path.isfile('clientcred.secret') == False):
        Mastodon.create_app('ace-attorney',
                            api_base_url=keys['domain'],
                            to_file='clientcred.secret')
    if (os.path.isfile('usercred.secret') == False):
        m = Mastodon(client_id='clientcred.secret',
                     api_base_url=keys['domain'])
        m.log_in(keys['mail'], keys['password'], to_file='usercred.secret')

    api = Mastodon(access_token='usercred.secret', api_base_url=keys['domain'])
Exemple #32
0
    def authorize(self):
        """
        Tries to authorize via OAuth API, and save access token. If it fails
        fallsback to username and password.
        """
        url = self.url
        client_secret = self.client_secret
        user_secret = self.user_secret
        scopes = self.scopes
        print("This app needs access to your Mastodon account.")

        mastodon = Mastodon(client_id=client_secret, api_base_url=url)

        url = mastodon.auth_request_url(client_id=client_secret, scopes=scopes)

        print("Visit the following URL and authorize the app:")
        print(url)

        print("Then paste the access token here:")
        token = sys.stdin.readline().rstrip()

        try:
            # on the very first login, --pace has no effect
            mastodon.log_in(code=token, to_file=user_secret, scopes=scopes)

        except Exception:

            print(
                "Sadly, that did not work. On some sites, this login mechanism"
            )
            print("(namely OAuth) seems to be broken. There is an alternative")
            print(
                "if you are willing to trust us with your password just this")
            print("once. We need it just this once in order to get an access")
            print(
                "token. We won't save it. If you don't want to try this, use")
            print("Ctrl+C to quit. If you want to try it, please provide your")
            print("login details.")

            sys.stdout.write("Email: ")
            sys.stdout.flush()
            email = sys.stdin.readline().rstrip()
            sys.stdout.write("Password: ")
            sys.stdout.flush()
            password = sys.stdin.readline().rstrip()

            # on the very first login, --pace has no effect
            mastodon.log_in(username=email,
                            password=password,
                            to_file=user_secret,
                            scopes=scopes)

        return mastodon
Exemple #33
0
    def crawl(self, user):
        """
        Crawl mentions from Mastodon.

        :return: list of statuses
        """
        mentions = []
        try:
            m = Mastodon(*user.get_masto_credentials())
        except TypeError:
            # logger.error("No Mastodon Credentials in database.", exc_info=True)
            return mentions
        try:
            notifications = m.notifications()
        except Exception:
            logger.error("Unknown Mastodon API Error.", exc_info=True)
            return mentions
        for status in notifications:
            if (status['type'] == 'mention'
                    and not user.toot_is_seen(status['status']['uri'])):
                # save state
                user.toot_witness(status['status']['uri'])
                # add mention to mentions
                text = re.sub(r'<[^>]*>', '', status['status']['content'])
                text = re.sub(
                    "(?<=^|(?<=[^a-zA-Z0-9-_.]))@([A-Za-z]+[A-Za-z0-9-_]+)",
                    "", text)
                if status['status']['visibility'] == 'public':
                    mentions.append(
                        Report(status['account']['acct'], self, text,
                               status['status']['id'],
                               status['status']['created_at']))
                else:
                    mentions.append(
                        Report(status['account']['acct'], 'mastodonPrivate',
                               text, status['status']['id'],
                               status['status']['created_at']))
        return mentions
    def _execute(self, options, args):
        """Announce today's post on Mastodon"""
        self.site.scan_posts()
        today_post = [
            x for x in self.site.timeline
            if x.date.date() == datetime.today().date()
        ]
        if len(today_post) > 0:
            meta = get_meta(today_post[0], 'fr')
            content = list([today_post[0].title()])
            content.append(today_post[0].description())
            [content.append(x) for x in meta[0]['references']]
            content.insert(0, '#laminuteculturelle')
            content.append(
                'archives: https://www.mad-scientists.net/la-minute-culturelle/'
            )
            content_str = "\n".join(content)

            mastodon = Mastodon(access_token=options['mastodon-token'],
                                api_base_url=options['mastodon-node'])
            mastodon.toot(content_str)

        return 0
Exemple #35
0
    def check(self, request, user):
        """
        check if the service is well configured
        :return: Boolean
        """
        redirect_uris = '%s://%s%s' % (request.scheme, request.get_host(), reverse('mastodon_callback'))
        us = UserService.objects.get(user=user,
                                     name='ServiceMastodon')
        client_id, client_secret = MastodonAPI.create_app(
            client_name="TriggerHappy", api_base_url=us.host,
            redirect_uris=redirect_uris)

        # get the token by logging in
        mastodon = MastodonAPI(
            client_id=client_id,
            client_secret=client_secret,
            api_base_url=us.host
        )
        try:
            mastodon.log_in(username=us.username, password=us.password)
            return True
        except MastodonIllegalArgumentError as e:
            return e
Exemple #36
0
def get_service(hass, config, discovery_info=None):
    """Get the Mastodon notification service."""
    from mastodon import Mastodon
    from mastodon.Mastodon import MastodonUnauthorizedError

    client_id = config.get(CONF_CLIENT_ID)
    client_secret = config.get(CONF_CLIENT_SECRET)
    access_token = config.get(CONF_ACCESS_TOKEN)
    base_url = config.get(CONF_BASE_URL)

    try:
        mastodon = Mastodon(
            client_id=client_id,
            client_secret=client_secret,
            access_token=access_token,
            api_base_url=base_url,
        )
        mastodon.account_verify_credentials()
    except MastodonUnauthorizedError:
        _LOGGER.warning("Authentication failed")
        return None

    return MastodonNotificationService(mastodon)
Exemple #37
0
    def __init__(self, access_token, since_at, instance_url, msg_template='', debug_mode=True, account_id=1):
        self.instance_url = instance_url
        self.msg_template = Template(msg_template)
        self.client = Mastodon(
            access_token=access_token,
            api_base_url=self.instance_url
        )
        self.since_at = since_at
        self.debug_mode = debug_mode

        self.log = logging.getLogger()
        if self.debug_mode:
            self.log.setLevel(logging.DEBUG)
        else:
            self.log.setLevel(logging.INFO)

        ch = logging.StreamHandler(sys.stdout)
        self.log.addHandler(ch)

        self.account_id = account_id

        self.until = pytz.utc.localize(datetime.now() - timedelta(days=1))
        self.until = self.until.replace(hour=23, minute=59, second=59)
Exemple #38
0
    def set_mastodon_keys(conn):
        msg = "In order to use Mastodon, we need to and can generate the keys here." + linefeed + linefeed
        print(msg)

        mastodon_instance_url = input("First, I need the url of the instance your bot account is on (EX: https://botsin.space): ")
        mastodon_bot_app_name = input("Now I need to know what you want to call the app. Try to make it unique (EX: my_aprs_bot): ")
        mastodon_username = input("Now I need the username for the bot account: ")
        mastodon_password = input("Finally I need the password for the bot account: ")

        client_keys = Mastodon.create_app(mastodon_bot_app_name, scopes=['read', 'write'], api_base_url=mastodon_instance_url)
        client_id = client_keys[0]
        client_secret = client_keys[1]

        mastodon_api = Mastodon(client_id, client_secret, api_base_url = mastodon_instance_url)

        access_token = mastodon_api.log_in(mastodon_username, mastodon_password, scopes=["read", "write"])

        sql = "update apikeys set mastodon_client_id = '" + client_id + "', "
        sql = sql + "mastodon_client_secret = '" + client_secret + "', "
        sql = sql + "mastodon_api_base_url = '" + mastodon_instance_url + "', "
        sql = sql + "mastodon_user_access_token = '" + access_token + "';"
        
        run_sql(conn,sql)
    def __init__(self):

        self.logger = logging.getLogger(__name__)

        self.project_root = os.path.abspath(os.path.dirname(__file__))

        self.config_file = os.path.join(self.project_root, "config.cfg")

        self.load_config()

        if self.verbose:
            self.logger.setLevel(logging.INFO)

        self.redis = redis.Redis(
            host=self.redis_hostname,
            port=self.redis_port,
            password=self.redis_password,
            charset="utf-8",
            decode_responses=True,
        )

        self.twitter_api = None
        self.mastodon_api = None

        if self.twitter_consumer_key:
            self.twitter_api = twitter.Api(
                consumer_key=self.twitter_consumer_key,
                consumer_secret=self.twitter_consumer_secret,
                access_token_key=self.twitter_access_token,
                access_token_secret=self.twitter_access_token_secret,
            )

        if self.mastodon_client_id:
            self.mastodon_api = Mastodon(
                client_id=self.mastodon_client_id,
                client_secret=self.mastodon_client_secret,
                access_token=self.mastodon_access_token,
                api_base_url=self.mastodon_api_base_url,
            )

        try:
            self.local_tz = pytz.timezone(self.timezone)
        except pytz.exceptions.UnknownTimeZoneError:
            self.error("Unknown or no timezone in settings: %s" % self.timezone)
            sys.exit(0)
Exemple #40
0
    def start(self, folder=".") -> None:
        "This actually runs the bot"
        self.mastodon = Mastodon(
            client_id=os.path.join(folder, 'pyborg_mastodon_clientcred.secret'),
            access_token=os.path.join(folder, 'pyborg_mastodon_usercred.secret'),
            api_base_url=self.settings['mastodon']['base_url']
        )
        self.my_id = self.mastodon.account_verify_credentials()['id']

        while True:
            tl = self.mastodon.timeline()
            toots: List[Dict] = []
            mentions = [notif['status'] for notif in self.mastodon.notifications() if notif['type'] == "mention"]
            toots.extend(tl)
            toots.extend(mentions)
            self.handle_toots(toots)
            self.last_look = arrow.utcnow()
            logger.debug("Sleeping for {} seconds".format(self.settings['mastodon']['cooldown']))
            time.sleep(self.settings['mastodon']['cooldown'])
Exemple #41
0
    return True

if __name__ == '__main__':
    from mastodon import Mastodon

    try:
        base_url, email, password, clientname, clientcred, usercred = sys.argv[1:]
    except:
        print("Usage: %s base_url email password clientname clientcredsfile usercredsfile" % sys.argv[0])
        sys.exit(2)

    if not os.path.isfile(clientcred):
        Mastodon.create_app(
            clientname,
            to_file = clientcred,
            scopes = [ 'read', 'write' ],
            api_base_url = base_url)

    if not os.path.isfile(usercred):
        mastodon_api = Mastodon(
            client_id = clientcred,
            api_base_url = base_url)

        mastodon_api.log_in(
            email,
            password,
            to_file = usercred,
            scopes = [ 'read', 'write' ],
            )
Exemple #42
0
    sys.exit(0)

# Load secrets from secrets file
secrets_filepath = "secrets/secrets.txt"
uc_client_id     = get_parameter("uc_client_id",     secrets_filepath)
uc_client_secret = get_parameter("uc_client_secret", secrets_filepath)
uc_access_token  = get_parameter("uc_access_token",  secrets_filepath)

# Load configuration from config file
config_filepath = "config.txt"
mastodon_hostname = get_parameter("mastodon_hostname", config_filepath) # E.g., mastodon.social

# Initialise Mastodon API
mastodon = Mastodon(
    client_id = uc_client_id,
    client_secret = uc_client_secret,
    access_token = uc_access_token
)

# Initialise access headers
headers={ 'Authorization': 'Bearer %s'%uc_access_token }


###############################################################################
# GET THE DATA
###############################################################################

# Get current timestamp
ts = int(time.time())

# Get the /about/more page from the server
Exemple #43
0
											# You can e.g. use https://takahashim.github.io/mastodon-access-token/
											# to generate your access-key.
#---------------------------------------------
#########################################################
# no need to change anything after here
#---------------------------------------------


# # #    M A I N    L O O P    # # # #
#---------------------------------------------
#
picdir = os.getcwd()
print('picture directory is %s' % (picdir))

mastodon = Mastodon(
    access_token = access_token,
    api_base_url = podurl 
)


# check if there is my archive_posted.txt in picture directory
archive_path = '%s/%s' % (picdir, '0archive_mastodon.txt')
print(archive_path)
ismylogthere = os.path.exists(archive_path)
if ismylogthere == False:
	print('creating my archive-log in %s' % (archive_path))
	f=open(archive_path,"w")
	f.close()


# read already posted pics from archive_posted.txt
archiveposted = open(archive_path).read()
Exemple #44
0
# o8o        o888o `Y888""8o o888o o888o o888o      o888ooooood8 `Y8bod8P' `Y8bod8P'  888bod8P' 
#                                                                                     888       
#                                                                                    o888o                                                                            

# TODO: remove class
class Ask:
    def __init__(self, message, sender, askid):
        self.sender = sender
        self.askid = askid
        self.message = message.encode('utf-8', 'ignore')
        self.message = filter_message(self.message)
        self.message = Message(self.message, self.sender)

# Create Mastodon API instance
mastodon = Mastodon(
    access_token = 'emma_usercred.secret',
    api_base_url = 'https://botsin.space'
)

# Create listener
class Listener(StreamListener):
    """
    Listens for Mastodon activity

    Class Variables
    message         str     String representation of the Message
    sender          str     Username of person who sent the Message
    tootID          int     ID of the Toot so that we can reply
    reply           str     Emma's reply to the Message
    """
    def on_notification(self, status):
        if status.type == 'mention':       
class Tweeter:

    twitter_consumer_key = ""
    twitter_consumer_secret = ""
    twitter_access_token = ""
    twitter_access_token_secret = ""

    mastodon_client_id = ""
    mastodon_client_secret = ""
    mastodon_access_token = ""
    mastodon_api_base_url = "https://mastodon.social"

    # 1 will output stuff.
    verbose = 0

    # How many years ahead are we of the dated tweets?
    years_ahead = 0

    # No matter when we last ran this script, we'll only ever post
    # tweets from within the past max_time_window minutes.
    max_time_window = 20

    # Which timezone are we using to check when tweets should be sent?
    # eg 'Europe/London'.
    # See http://en.wikipedia.org/wiki/List_of_tz_database_time_zones for
    # possible strings.
    timezone = "Europe/London"

    # Only used if we're using Redis.
    redis_hostname = "localhost"
    redis_port = 6379
    redis_password = None
    # Will be the redis.Redis() object:
    redis = None

    def __init__(self):

        self.logger = logging.getLogger(__name__)

        self.project_root = os.path.abspath(os.path.dirname(__file__))

        self.config_file = os.path.join(self.project_root, "config.cfg")

        self.load_config()

        if self.verbose:
            self.logger.setLevel(logging.INFO)

        self.redis = redis.Redis(
            host=self.redis_hostname,
            port=self.redis_port,
            password=self.redis_password,
            charset="utf-8",
            decode_responses=True,
        )

        self.twitter_api = None
        self.mastodon_api = None

        if self.twitter_consumer_key:
            self.twitter_api = twitter.Api(
                consumer_key=self.twitter_consumer_key,
                consumer_secret=self.twitter_consumer_secret,
                access_token_key=self.twitter_access_token,
                access_token_secret=self.twitter_access_token_secret,
            )

        if self.mastodon_client_id:
            self.mastodon_api = Mastodon(
                client_id=self.mastodon_client_id,
                client_secret=self.mastodon_client_secret,
                access_token=self.mastodon_access_token,
                api_base_url=self.mastodon_api_base_url,
            )

        try:
            self.local_tz = pytz.timezone(self.timezone)
        except pytz.exceptions.UnknownTimeZoneError:
            self.error("Unknown or no timezone in settings: %s" % self.timezone)
            sys.exit(0)

    def load_config(self):
        if os.path.isfile(self.config_file):
            self.load_config_from_file()
        else:
            self.load_config_from_env()

    def load_config_from_file(self):
        config = configparser.ConfigParser()
        config.read(self.config_file)

        settings = config["DEFAULT"]

        self.twitter_consumer_key = settings["TwitterConsumerKey"]
        self.twitter_consumer_secret = settings["TwitterConsumerSecret"]
        self.twitter_access_token = settings["TwitterAccessToken"]
        self.twitter_access_token_secret = settings["TwitterAccessTokenSecret"]

        self.mastodon_client_id = settings["MastodonClientId"]
        self.mastodon_client_secret = settings["MastodonClientSecret"]
        self.mastodon_access_token = settings["MastodonAccessToken"]
        self.mastodon_api_base_url = settings["MastodonApiBaseUrl"]

        self.verbose = int(settings.get("Verbose", self.verbose))
        self.years_ahead = int(settings.get("YearsAhead", self.years_ahead))
        self.timezone = settings.get("Timezone", self.timezone)
        self.max_time_window = int(settings.get("MaxTimeWindow", self.max_time_window))

        self.redis_hostname = settings.get("RedisHostname", self.redis_hostname)
        self.redis_port = int(settings.get("RedisPort", self.redis_port))
        self.redis_password = settings.get("RedisPassword", self.redis_password)

    def load_config_from_env(self):
        self.twitter_consumer_key = os.environ.get("TWITTER_CONSUMER_KEY")
        self.twitter_consumer_secret = os.environ.get("TWITTER_CONSUMER_SECRET")
        self.twitter_access_token = os.environ.get("TWITTER_ACCESS_TOKEN")
        self.twitter_access_token_secret = os.environ.get("TWITTER_ACCESS_TOKEN_SECRET")

        self.mastodon_client_id = os.environ.get("MASTODON_CLIENT_ID")
        self.mastodon_client_secret = os.environ.get("MASTODON_CLIENT_SECRET")
        self.mastodon_access_token = os.environ.get("MASTODON_ACCESS_TOKEN")
        self.mastodon_api_base_url = os.environ.get("MASTODON_API_BASE_URL")

        self.verbose = int(os.environ.get("VERBOSE", self.verbose))
        self.years_ahead = int(os.environ.get("YEARS_AHEAD", self.years_ahead))
        self.timezone = os.environ.get("TIMEZONE", self.timezone)
        self.max_time_window = int(
            os.environ.get("MAX_TIME_WINDOW", self.max_time_window)
        )

        redis_url = urlparse.urlparse(os.environ.get("REDIS_URL"))
        self.redis_hostname = redis_url.hostname
        self.redis_port = redis_url.port
        self.redis_password = redis_url.password

    def start(self):

        # eg datetime.datetime(2014, 4, 25, 18, 59, 51, tzinfo=<UTC>)
        last_run_time = self.get_last_run_time()

        # We need to have a last_run_time set before we can send any tweets.
        # So the first time this is run, we can't do anythning.
        if last_run_time is None:
            self.set_last_run_time()
            logging.warning(
                "No last_run_time in database.\n"
                "This must be the first time this has been run.\n"
                "Settinge last_run_time now.\n"
                "Run the script again in a minute or more, and it should work."
            )
            sys.exit(0)

        local_time_now = datetime.datetime.now(self.local_tz)

        year_dir = str(int(local_time_now.strftime("%Y")) - self.years_ahead)
        month_file = "%s.txt" % local_time_now.strftime("%m")

        # eg tweets/1660/01.txt
        path = os.path.join(self.project_root, "tweets", year_dir, month_file)

        with open(path) as file:
            lines = [line.strip() for line in file]

        all_tweets = self.get_all_tweets(lines)

        tweets_to_send = self.get_tweets_to_send(
            all_tweets, last_run_time, local_time_now
        )

        self.set_last_run_time()

        # We want to tweet the oldest one first, so reverse list:
        self.send_tweets(tweets_to_send[::-1])

        # And the same with Mastodon toots:
        self.send_toots(tweets_to_send[::-1])

    def get_all_tweets(self, lines):
        """
        Go through all the lines in the file and, for any that contain
        valid tweet data, add them to a list to return.

        Returns a list of dicts, each one data about a tweet.
        """
        tweets = []

        for line in lines:

            if line != "":
                tweet = self.parse_tweet_line(line)

                if tweet:
                    tweets.append(tweet)
                else:
                    # An invalid line format or invalid tweet time.
                    continue

        return tweets

    def get_tweets_to_send(self, all_tweets, last_run_time, local_time_now):
        """
        Work out which of all the tweets in the month need to be sent.

        all_tweets - List of dicts, one per tweet
        last_run_time - datetime object for when the script was last run
        local_time_now - timezone-aware datetime for now

        Returns a list of dicts of the tweets that need sending.
        """

        tweets_to_send = []

        local_last_run_time = last_run_time.astimezone(self.local_tz)

        for n, tweet in enumerate(all_tweets):

            local_modern_tweet_time = self.modernize_time(tweet["time"])
            now_minus_tweet = (local_time_now - local_modern_tweet_time).total_seconds()
            tweet_minus_lastrun = (
                local_modern_tweet_time - local_last_run_time
            ).total_seconds()

            if now_minus_tweet > 0:
                # Tweet is earlier than now.
                if tweet_minus_lastrun >= 0 and now_minus_tweet <= (
                    self.max_time_window * 60
                ):
                    # And Tweet is since we last ran and within our max time window.

                    if tweet["is_reply"] is True:
                        # Get the time of the previous tweet, which is the one
                        # this tweet is replying to.
                        prev_tweet = all_tweets[n + 1]
                        in_reply_to_time = prev_tweet["time"]
                    else:
                        in_reply_to_time = None

                    tweet["in_reply_to_time"] = in_reply_to_time

                    self.log(
                        "Preparing: '{}...' "
                        "timed {}, "
                        "is_reply: {}, "
                        "local_last_run_time: {}, "
                        "local_modern_tweet_time: {}, "
                        "in_reply_to_time: {}".format(
                            tweet["text"][:20],
                            tweet["time"],
                            tweet["is_reply"],
                            local_last_run_time,
                            local_modern_tweet_time,
                            in_reply_to_time,
                        )
                    )

                    tweets_to_send.append(tweet)
                else:
                    break

        return tweets_to_send

    def parse_tweet_line(self, line):
        """
        Given one line from a text file, try to parse it out into time and
        tweet text.

        Returns a dict of data if successful, otherwise False

        A line is like one of:

        1666-02-09 14:08 This is my text
        1666-02-09 14:08   This is my text
        1666-02-09 14:08 r This is my text
        1666-02-09 14:08 r   This is my text
        """
        tweet = False

        pattern = r"""
            ^                           # Start of line
            (
                \d\d\d\d-\d\d-\d\d      # Date like 1666-02-09
                \s
                \d\d\:\d\d              # Time like 14:08
            )                           # GROUP 1: Date and time
            (?:                         # Don't count this group
                \s                      # A space before the 'r'
                (
                    \w                   # A literal 'r' (probably).
                )                       # GROUP 2: r (or None)
            )?                          # The 'r ' is optional
            \s+                         # One or more spaces
            (.*?)                       # The tweet text
            $                           # End of line
        """

        line_match = re.search(pattern, line, re.VERBOSE)

        if line_match:
            [tweet_time, tweet_kind, tweet_text] = line_match.groups()

            # Check the time maps to a valid modern time:
            local_modern_tweet_time = self.modernize_time(tweet_time)

            if local_modern_tweet_time:

                if tweet_kind == "r":
                    is_reply = True
                else:
                    is_reply = False

                tweet = {
                    "time": tweet_time,
                    "text": tweet_text.strip(),
                    "is_reply": is_reply,
                }

        return tweet

    def set_last_run_time(self):
        """
        Set the 'last run time' in the database to now, in UTC.
        """
        time_now = datetime.datetime.now(pytz.timezone("UTC"))
        self.redis.set("last_run_time", time_now.strftime("%Y-%m-%d %H:%M:%S"))

    def get_last_run_time(self):
        """
        Get the 'last run time' from the database.
        Returns, eg
        datetime.datetime(2014, 4, 25, 18, 59, 51, tzinfo=<UTC>)
        or `None` if it isn't currently set.
        """
        last_run_time = self.redis.get("last_run_time")

        if last_run_time:
            return datetime.datetime.strptime(
                last_run_time, "%Y-%m-%d %H:%M:%S"
            ).replace(tzinfo=pytz.timezone("UTC"))
        else:
            return None

    def modernize_time(self, t):
        """
        Takes a time string like `1661-04-28 12:34` and translates it to the
        modern equivalent in local time, eg:
        datetime.datetime(
            2014, 4, 28, 12, 34, 00,
            tzinfo=<DstTzInfo 'Europe/London' BST+1:00:00 DST>)
        Returns False if something goes wrong.
        """
        naive_time = datetime.datetime.strptime(t, "%Y-%m-%d %H:%M")
        try:
            local_modern_time = self.local_tz.localize(
                datetime.datetime(
                    naive_time.year + self.years_ahead,
                    naive_time.month,
                    naive_time.day,
                    naive_time.hour,
                    naive_time.minute,
                    naive_time.second,
                )
            )
        except ValueError as e:
            # Unless something else is wrong, it could be that naive_time
            # is 29th Feb and there's no 29th Feb in the current, modern, year.
            self.log("Skipping %s as can't make a modern time from it: %s" % (t, e))
            local_modern_time = False

        return local_modern_time

    def send_tweets(self, tweets):
        """
        `tweets` is a list of tweets to post now.

        Each element is a dict of:
            'time' (e.g. '1666-02-09 12:35')
            'text' (e.g. "This is my tweet")
            'is_reply_to' (e.g. '1666-02-09 12:34' or '')
            'in_reply_to_time' (e.g. '1666-02-09 12:33', or None)

        Should be in the order in which they need to be posted.
        """
        if self.twitter_api is None:
            self.log("No Twitter Consumer Key set; not tweeting")
            return

        for tweet in tweets:
            previous_status_id = None

            if tweet["in_reply_to_time"] is not None:
                # This tweet is a reply, so check that it's a reply to the
                # immediately previous tweet.
                # It *should* be, but if something went wrong, maybe not.
                previous_status_time = self.redis.get("previous_tweet_time")

                if tweet["in_reply_to_time"] == previous_status_time:
                    previous_status_id = self.redis.get("previous_tweet_id")

            self.log(
                "Tweeting: {} [{} characters]".format(tweet["text"], len(tweet["text"]))
            )

            try:
                status = self.twitter_api.PostUpdate(
                    tweet["text"], in_reply_to_status_id=previous_status_id
                )
            except twitter.TwitterError as e:
                self.error(e)
            else:
                # Set these so that we can see if the next tweet is a reply
                # to this one, and then one ID this one was.
                self.redis.set("previous_tweet_time", tweet["time"])
                self.redis.set("previous_tweet_id", status.id)

            time.sleep(2)

    def send_toots(self, tweets):
        """
        `tweets` is a list of toot texts to post now.

        Each element is a dict of:
            'time' (e.g. '1666-02-09 12:35')
            'text' (e.g. "This is my toot")
            'is_reply' boolean; is this a reply to the previous toot.
            'in_reply_to_time' (e.g. '1666-02-09 12:33', or None)

        Should be in the order in which they need to be posted.
        """
        if self.mastodon_api is None:
            self.log("No Mastodon Client ID set; not tooting")
            return

        for tweet in tweets:
            previous_status_id = None

            if tweet["in_reply_to_time"] is not None:
                # This tweet is a reply, so check that it's a reply to the
                # immediately previous toot.
                # It *should* be, but if something went wrong, maybe not.
                previous_status_time = self.redis.get("previous_toot_time")

                if tweet["in_reply_to_time"] == previous_status_time:
                    previous_status_id = self.redis.get("previous_toot_id")

            self.log(
                "Tooting: {} [{} characters]".format(tweet["text"], len(tweet["text"]))
            )

            try:
                status = self.mastodon_api.status_post(
                    tweet["text"], in_reply_to_id=previous_status_id
                )
            except MastodonError as e:
                self.error(e)
            else:
                # Set these so that we can see if the next tweet is a reply
                # to this one, and then one ID this one was.
                self.redis.set("previous_toot_time", tweet["time"])
                self.redis.set("previous_toot_id", status.id)

            time.sleep(2)

    def log(self, s):
        self.logger.info(s)

    def error(self, s):
        self.logger.error(s)
Exemple #46
0
class PyborgMastodon(object):
    """it does toots"""

    # todo: attrs
    def __init__(self, conf_file):
        self.toml_file = conf_file
        self.settings = toml.load(conf_file)
        self.last_look = arrow.get(self.settings['mastodon']['last_look'])
        self.multiplexing = True
        self.multi_server = self.settings['pyborg']['multi_server']

    def teardown(self) -> None:
        self.settings['mastodon']['last_look'] = self.last_look.datetime
        with open(self.toml_file, "w") as f:
            toml.dump(self.settings, f)
            logging.info("saved mastodon config at: %s", self.toml_file)
        if not self.multiplexing:
            raise NotImplementedError("Use multiplexing.")
            # self.pyborg.save_all()

    def learn(self, body) -> None:
        if self.multiplexing:
            try:
                ret = requests.post("http://{}:2001/learn".format(self.multi_server), data={"body": body})
                if ret.status_code > 499:
                    logger.error("Internal Server Error in pyborg_http. see logs.")
                else:
                    ret.raise_for_status()
            except requests.exceptions.ConnectionError as e:
                logger.exception(e)
                self.teardown()
                sys.exit(17)
        else:
            raise NotImplementedError("Use multiplexing.")
            # self.pyborg.learn(body)

    def reply(self, body) -> Optional[str]:
        if self.multiplexing:
            try:
                ret = requests.post("http://{}:2001/reply".format(self.multi_server), data={"body": body})
                if ret.status_code == requests.codes.ok:
                    reply = ret.text
                    logger.debug("got reply: %s", reply)
                elif ret.status_code > 499:
                    logger.error("Internal Server Error in pyborg_http. see logs.")
                    return None
                else:
                    ret.raise_for_status()
                return reply
            except requests.exceptions.ConnectionError as e:
                logger.exception(e)
                self.teardown()
                sys.exit(17)
        else:
            raise NotImplementedError("use multiplexing.")
            # return self.pyborg.reply(body)

    def should_reply_direct(self, usern) -> bool:
        should_reply = []
        should_reply.extend([a['acct'] for a in self.mastodon.account_followers(self.my_id)])  # is this cached?
        return usern in should_reply

    def is_reply_to_me(self, item: Dict) -> bool:
        logger.debug(item)
        try:
            if item["in_reply_to_account_id"] == self.my_id:
                return True
            else:
                return False
        except KeyError:
            if item["type"] == "mention":
                if any([True for mention in item["mentions"] if mention['id'] == self.my_id]):
                    return True
                else:
                    # Is this actually possible?
                    return False
            else:
                return False

    def handle_toots(self, toots: List[Dict]) -> None:
        for item in toots:
            # logger.debug(arrow.get(item["created_at"]) > self.last_look)
            logger.debug(item['content'])
            logger.debug(arrow.get(item["created_at"]) - self.last_look)
            if (arrow.get(item["created_at"]) > self.last_look) and item["account"]["id"] is not self.my_id:
                logger.info("Got New Toot: {}".format(item))
                fromacct = item['account']['acct']  # to check if we've banned them?
                parsed_html = lxml.html.fromstring(item['content'])
                body = parsed_html.text_content()
                if self.settings['pyborg']['learning']:
                    self.learn(body)
                reply = self.reply(body)
                if reply:
                    logger.debug("got reply from http: %s", reply)
                    if (self.should_reply_direct(fromacct) or self.is_reply_to_me(item)):
                        self.mastodon.status_post(reply, in_reply_to_id=item['id'])
                    else:
                        logger.info("Got reply but declined to toot. recv'd body: %s", body)
                else:
                    logger.info("Couldn't toot.")

    def start(self, folder=".") -> None:
        "This actually runs the bot"
        self.mastodon = Mastodon(
            client_id=os.path.join(folder, 'pyborg_mastodon_clientcred.secret'),
            access_token=os.path.join(folder, 'pyborg_mastodon_usercred.secret'),
            api_base_url=self.settings['mastodon']['base_url']
        )
        self.my_id = self.mastodon.account_verify_credentials()['id']

        while True:
            tl = self.mastodon.timeline()
            toots: List[Dict] = []
            mentions = [notif['status'] for notif in self.mastodon.notifications() if notif['type'] == "mention"]
            toots.extend(tl)
            toots.extend(mentions)
            self.handle_toots(toots)
            self.last_look = arrow.utcnow()
            logger.debug("Sleeping for {} seconds".format(self.settings['mastodon']['cooldown']))
            time.sleep(self.settings['mastodon']['cooldown'])
Exemple #47
0
def send_toot(ticket, config):
    logging.info("toot the release")

    target = ''
    if ticket.voctoweb_enable and ticket.profile_voctoweb_enable:
        target = config['voctoweb']['instance_name']
    if ticket.youtube_enable and ticket.profile_youtube_enable:
        if len(target) > 1:
            target += ' and '
        target += 'YouTube'

    msg = ' has been released on ' + target
    title = ticket.title
    if len(title) >= (500 - len(msg)):
        title = title[0:len(msg)]
    message = title + msg

    if ticket.voctoweb_enable and ticket.profile_voctoweb_enable:
        voctoweb_url = ' ' + config['voctoweb']['frontend_url'] + '/v/' + ticket.slug
        if len(voctoweb_url) <= (500 - len(message)):
            message = message + voctoweb_url
    if ticket.youtube_enable and ticket.profile_youtube_enable and len(ticket.youtube_urls['YouTube.Url0']) <= (500 - len(message)):
        message = message + ' ' + ticket.youtube_urls['YouTube.Url0']

    try:
        # check if we already have our client token and secret and if not get a new one
        if not Path('./mastodon_clientcred.secret').exists():
            logging.debug('no mastodon client credentials found, get fresh ones')
            Mastodon.create_app(
                'voctopublish',
                api_base_url=config['mastodon']['api_base_url'],
                to_file='mastodon_clientcred.secret'
            )
        else:
            logging.debug('Using exisiting Mastodon client credentials')

        mastodon = Mastodon(
            client_id='mastodon_clientcred.secret',
            api_base_url=config['mastodon']['api_base_url']
        )

        # check if we already have an access token, if not get a fresh one
        if not Path('./mastodon_usercred.secret').exists():
            logging.debug('no mastodon user credentials found, getting a fresh token')
            mastodon.log_in(
                config['mastodon']['email'],
                config['mastodon']['password'],
                to_file='mastodon_usercred.secret'
            )
        else:
            logging.debug('Using existing Mastodon user token')

        # Create actual API instance
        mastodon = Mastodon(
            access_token='mastodon_usercred.secret',
            api_base_url=config['mastodon']['api_base_url']
        )
        mastodon.toot(message)
    except Exception as e_:
        # we don't care if tooting fails here.
        logging.error('Tooting failed: ' + str(e_))
Exemple #48
0
last_id_file = Path('.') / 'last_id.txt'
last_id = 0

if IS_DOCKER:
    last_id_file = Path('/data') / 'last_id'
if last_id_file.is_file():
    with open(last_id_file) as f:
        last_id = int(f.read())

print('Botvenon is starting...')
print('Welcoming any users after id %d' % last_id)

mastodon = Mastodon(
    client_id=CLIENT_ID,
    client_secret=CLIENT_SECRET,
    access_token=ACCESS_TOKEN,
    api_base_url=INSTANCE_BASE)

user = mastodon.account_verify_credentials()  # Get the current user

followers = mastodon.account_followers(user)  # Get the latest followers


# print(
#     f'''Followed by: {account.username}
# (Acct value: {account.acct}, id: {account.id})''')

while True:
    for account in mastodon.account_followers(user):
        if is_local_account(account) and account.id > last_id and not account.bot: