def isfav(username, uuid, instance, secret): response.headers['Access-Control-Allow-Origin'] = '*' response.content_type = "application/json" suid = str(r.get("nordcast/uuids/" + username + "$$" + instance)).replace( "b'", "").replace("'", "") try: mastodon = Mastodon(access_token='authtokens/' + username + '.' + instance + '.secret', api_base_url='https://' + instance) mastodon.account_verify_credentials().source.note except: pass if uuid in suid: isfav = str( r.get("nordcast/isfav/" + username + "$$" + instance + "/" + secret)).replace("b'", "").replace("'", "") if isfav == "true": isfav = True else: isfav = False return json.dumps({ "login": "******", "uuid": uuid, "action": "success", "isfav": isfav, "secret": secret })
def login(pool): instance = pool.mastInstance user = pool.mastUser appName = pool.mastAppName secPath = pool.mastSecPath userCredFile = os.path.join(secPath, "ghi_usercred.secret") try: if os.path.isfile(userCredFile): mastodon = Mastodon(access_token=userCredFile, api_base_url=instance) mastodon.account_verify_credentials() logging.info( "Mastodon - Connected to {instance} as user {user} with appname {appname}" .format(instance=instance, user=user, appname=appName)) else: logging.info("Mastodon - Creating credential-files.") createCreds(pool) return login(pool) except MastodonUnauthorizedError: notice = "Mastodon - Invalid user-credentials: recreating credential-files." logging.info(notice) createCreds(pool) return login(pool) return mastodon
def __init__(self, debug=False): super().__init__( sn_key=SocialNetworkType.mastodon, debug=debug, ) instance_keys_string = os.environ.get(MASTODON_INSTANCE_KEYS, None) if not instance_keys_string: raise RuntimeError("No Mastodon instances?") instance_keys = instance_keys_string.split(",") for instance_key in instance_keys: _instance_url_key = f"MASTODON_{instance_key}_URL" _instance_token_key = f"MASTODON_{instance_key}_TOKEN" instance_url = os.environ.get(_instance_url_key, None) instance_token = os.environ.get(_instance_token_key, None) if instance_url and instance_token: try: instance = MastodonPy( access_token=instance_token, api_base_url=instance_url, version_check_mode="none", ) instance.account_verify_credentials() self.instances.append(instance) except (MastodonError, Exception): pass if not self.instances: raise RuntimeError( "No (valid) Mastodon instances could be loaded :(")
def checkStatus(): mstdn = Mastodon(client_id=session['client_id'], client_secret=session['client_secret'], access_token=session['access_token'], api_base_url=session['uri']) id = mstdn.account_verify_credentials()["id"] scnt = mstdn.account_verify_credentials()["statuses_count"] return (id, scnt)
def login2(username, uuid, instance): response.headers['Access-Control-Allow-Origin'] = '*' response.content_type = "application/json" suid = str(r.get("nordcast/uuids/" + username + "$$" + instance)).replace( "b'", "").replace("'", "") try: mastodon = Mastodon(access_token='authtokens/' + username + '.' + instance + '.secret', api_base_url='https://' + instance) mastodon.account_verify_credentials().source.note except: pass if uuid in suid: return json.dumps({"login": "******", "uuid": uuid}) else: return "{\"login\": \"error\"}"
def main(email, password): register_app() mastodon = Mastodon(client_id=APP_PATH, access_token=APP_CRED) if email and password: login(mastodon, email, password) elif not authenticated(mastodon): email = input("Email used to login: "******"Invalid command. Use 'help' for a \ list of commands.") print("Welcome to tootstream!") print("Enter a command. Use 'help' for a list of commands.") print("\n") user = mastodon.account_verify_credentials() prompt = "[@" + str(user['username']) + "]: " while True: command = input(prompt).split(' ', 1) rest = "" try: rest = command[1] except IndexError: pass command = command[0] cmd_func = commands.get(command, say_error) cmd_func(mastodon, rest)
def auth(): # Currently, only for closed.social code = request.args.get('code') client = Mastodon( client_id = app.config['CLIENT_ID'], client_secret = app.config['CLIENT_SECRET'], api_base_url = app.config['MASTODON_URL'] ) token = client.log_in( code=code, redirect_uri=app.config['REDIRECT_URI'], scopes=['read:accounts'] ) info = client.account_verify_credentials() name = 'cs_' + str(info.id) u = v = User.query.filter_by(name=name).first() if not u: u = User(name=name) db.session.add(u) if not v or False: #TODO: reset token u.token = ''.join(random.choices(string.ascii_letters + string.digits, k=16)) db.session.commit() return redirect('/?token='+ u.token)
def mastodon_token(): if not app.config['ENABLE_MASTODON']: return abort(requests.codes.not_found) current_auth = db.session.query(MastodonAuth).filter( MastodonAuth.user_id == current_user.get_id()).first() mastodon_app = current_auth.app mastodon = Mastodon( client_id=mastodon_app.client_id, client_secret=mastodon_app.client_secret, api_base_url=mastodon_app.base_url(), ) try: access_token = mastodon.log_in( code=request.json['authorization_code'], scopes=['read'], redirect_uri=urljoin(app.config['MASTODON_REDIRECT_BASE_URL'], 'mastodon_auth_complete'), ) account = mastodon.account_verify_credentials() except MastodonAPIError: return abort(requests.codes.server_error) current_auth.update_account(access_token, account['id'], account['username']) current_user.mastodon_authorized = True db.session.commit() tasks.get_mastodon_posts_per_user.delay(current_user.get_id()) return 'success', 200
def authenticate(base_url=None, client_id=None, client_secret=None): if base_url is None: base_url = (input('Instance base URL [https://mastodon.social]: ') or 'https://mastodon.social') if client_id is None: client_id = input('Enter your client id: ') if client_secret is None: client_secret = input('Enter your client secret: ') api_unauth = Mastodon(api_base_url=base_url, client_id=client_id, client_secret=client_secret) print( 'Visit the following link, authenticate, and bring back the code that the page gives you afterwards.' ) print('NB: that code is *not* your access token.') print(api_unauth.auth_request_url(scopes=['read', 'write'])) code = input('> ') access_token = api_unauth.log_in(code=code, scopes=['read', 'write']) print('Got access token:', access_token) print('Testing...') api_auth = Mastodon(api_base_url=base_url, client_id=client_id, client_secret=client_secret, access_token=access_token) creds = api_auth.account_verify_credentials() print('Success, authenticated as {}.'.format(creds['username']))
def getname(username, uuid, instance): response.headers['Access-Control-Allow-Origin'] = '*' response.content_type = "application/json" suid = str(r.get("nordcast/uuids/" + username + "$$" + instance)).replace( "b'", "").replace("'", "") if not uuid == "dummy": mastodon = Mastodon(access_token='authtokens/' + username + '.' + instance + '.secret', api_base_url='https://' + instance) userdict = mastodon.account_verify_credentials() try: if uuid in suid: ksname = userdict.display_name ksemojis = userdict.emojis return json.dumps({ "login": "******", "uuid": uuid, "action": "success", "ksname": ksname, "ksemojis": ksemojis }) else: return "{\"login\": \"error\"}" except: return "{\"login\": \"error\"}"
class MastoCrosspostUtils: def __init__(self, clientcred_key, access_token_key, instance_url): self.mastodon_api = Mastodon( client_id=clientcred_key, access_token=access_token_key, api_base_url=instance_url ) self.me = self.mastodon_api.account_verify_credentials() def scrape_toots(self, mstdn_acct_id, since=None): """ Get toots from an account since given toot id and filter them """ toots = self.mastodon_api.account_statuses( mstdn_acct_id, since_id=since, exclude_replies=True) filtered_toots = [] if len(toots): filtered_toots = list(filter(lambda x: x['reblog'] is None and x['poll'] is None and x['visibility'] in [ "public", "unlisted", "private"], toots[::-1])) return filtered_toots def get_following(self): return self.mastodon_api.account_following(self.me.id)
def toot_on_mastodon(configs: dict, post_text: str, image_filenames: [str], reply_to_latest_post: bool = False): api = Mastodon(configs['mastodon']['client_id'], configs['mastodon']['client_secret'], configs['mastodon']['access_token'], configs['mastodon']['instance_url']) post_media = [] for filename in image_filenames: with open(filename, 'rb') as f: post_media.append(api.media_post(f.read(), 'image/png')) latest_status_id = None success_message = "tooted on Mastodon" if reply_to_latest_post: account_id = api.account_verify_credentials()['id'] latest_status = api.account_statuses(account_id, limit=1)[0] latest_status_id = latest_status['id'] success_message = ("replied to %s" % shorten_text(latest_status['content'])) api.status_post(post_text, in_reply_to_id=latest_status_id, media_ids=post_media) print(success_message)
def ensure_app_config(url_file, client_file, user_file): if not os.path.isfile(url_file): print("No settings found.") base_url = input("Instance URL: ") user = input("E-Mail: ") password = getpass.getpass("Password: "******"w") as f: f.write(base_url) else: print("Whoops, that went wrong - try again.") sys.exit(0) except: print("Whoops, that went wrong - try again.") sys.exit(0)
def __init__(self, api: mastodon.Mastodon, db_session): super().__init__() self.api = api self.db_session = db_session self.logger = logging.getLogger() self.me = api.account_verify_credentials() print(self.me.acct)
class Mastodon(collections.abc.Iterable): """ An implementation of Stream for communicating by Mastodon. Attributes ---------- logger: logging.logger A logger instance dealing with messages sent by this instance. api: mastodon.Mastodon A Mastodon instance wrapping APIs of the connected Mastodon instance. """ 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 __iter__(self) -> Iterator: listener = _TootListener() self.api.stream_user(listener, run_async=True) self._cached = listener.cache return iter(listener) def post(self, response: Response) -> bool: self.logger.info("Trying to toot: " + response.text) if response.message is not None and response.message.id_ in self._cached: in_reply_to = self._cached[response.message.id_] if self.reply_everyone: for user in reversed(in_reply_to['mentions']): if user['id'] != self.myself['id']: response.text = '@%s %s' % (user['acct'], response.text) response.text = '@%s %s' % (in_reply_to['account']['acct'], response.text) if len(response.text) > TOOT_LIMIT: self.logger.error( 'Length of given status has exceeded the limit: %d' % len(response.text)) return False try: if response.message is None: result = self.api.status_post(response.text) else: result = self.api.status_post( response.text, in_reply_to_id=response.message.id_) self.logger.info('Updated: ' + str(result)) except MastodonError: self.logger.error('An API error has occured.') return False return True
def get_auth(): uuid, code = get_redirect_params() if uuid is '' or code is '': flash('情報の取得に失敗しました(uuid, code)', 'error') return redirect(redirect_url, code=302) info = load_from_uuid(uuid) if info is None: flash('情報の取得に失敗しました(info)', 'error') return redirect(redirect_url, code=302) disable_uuid(uuid) user_id, domain = info client_id, client_secret = get_oauth_applications(domain) if client_id is '' or client_secret is '': flash('情報の取得に失敗しました(cilent_id, client_secret)', 'error') return redirect(redirect_url, code=302) api_base_url = get_api_base_url(domain) mastodon = Mastodon(client_id=client_id, client_secret=client_secret, api_base_url=api_base_url) try: access_token = mastodon.log_in(code=code, scopes=scopes, redirect_uri=redirect_url) except: flash('情報の取得に失敗しました(access_token)', 'error') return redirect(redirect_url, code=302) try: mastodon_account = mastodon.account_verify_credentials() except: flash('情報の取得に失敗しました(mastodon_account)', 'error') return redirect(redirect_url, code=302) session_id = get_random_str() session['session_id'] = session_id user = User.first(access_token=access_token) if user is None: user = User.first(user_id=user_id, domain=domain) if user is not None: user.access_token = access_token user.update() flash('Sign Inしています!', 'info') else: user = User(None, access_token, user_id, domain, mastodon_account.avatar) user.save() flash('Sign Inしました!', 'info') else: flash('Sign Inしています!', 'info') user.add_session(session_id) return redirect(redirect_url, code=302)
def get(self, request, *args, **kwargs): config = Config.load() if config.access_token: try: mastodon_client = Mastodon( client_id=config.client_id, client_secret=config.client_secret, access_token=config.access_token, api_base_url="https://" + config.instance, ) account_details = mastodon_client.account_verify_credentials() updated = False if config.username != account_details.username: config.username = account_details.username updated = True if config.display_name != account_details.display_name: config.display_name = account_details.display_name updated = True if config.avatar != account_details.avatar: config.avatar = account_details.avatar updated = True if updated: config.save() return JsonResponse({"status": "ok", "result": "updated"}) else: return JsonResponse({ "status": "ok", "result": "not_modified" }) except MastodonUnauthorizedError as e: reset_access_token(config) config.save() NabMastodond.signal_daemon() return HttpResponse( "Unauthorized", content=f'{{"status":"error",' f'"result":"unauthorized",' f'"message":"{e}"}}', mimetype="application/json", status=401, ) except MastodonError as e: return HttpResponse( "Unknown error", content=f'{{"status":"error","message":"{e}"}}', mimetype="application/json", status=500, ) else: return HttpResponse( "Not found", content='{"status":"error","result":"not_found"}', mimetype="application/json", status=404, )
def toot(username, uuid, instance, visibility): response.headers['Access-Control-Allow-Origin'] = '*' response.content_type = "application/json" suid = str(r.get("nordcast/uuids/" + username + "$$" + instance)).replace( "b'", "").replace("'", "") content = request.forms.get("content") # pylint: disable=no-member try: mastodon = Mastodon(access_token='authtokens/' + username + '.' + instance + '.secret', api_base_url='https://' + instance) mastodon.account_verify_credentials().source.note except: pass if uuid in suid: if "%20" in content: content = urllib.parse.unquote(content) mastodon.status_post(content, visibility=visibility) return json.dumps({"login": "******", "uuid": uuid, "action": "success"}) else: return "{\"login\": \"error\"}"
def get_service(hass, config, discovery_info=None): """Get the Mastodon notification service.""" 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)
def old_login(request): if request.method == "GET": form = LoginForm() return render(request, 'setup/login.html', {'form': form}) elif request.method == "POST": form = LoginForm(request.POST) if form.is_valid(): api_base_url = form.cleaned_data['instance'] tmp_base = parse.urlparse(api_base_url.lower()) if tmp_base.netloc == '': api_base_url = parse.urlunparse( ('https', tmp_base.path, '', '', '', '')) else: api_base_url = api_base_url.lower() request.session['instance'] = api_base_url username = form.cleaned_data['username'] password = form.cleaned_data['password'] try: client = Client.objects.get(api_base_id=api_base_url) except (Client.DoesNotExist, Client.MultipleObjectsReturned): (client_id, client_secret) = Mastodon.create_app( 'brutaldon', api_base_url=api_base_url) client = Client(api_base_id=api_base_url, client_id=client_id, client_secret=client_secret) client.save() mastodon = Mastodon(client_id=client.client_id, client_secret=client.client_secret, api_base_url=api_base_url) try: account = Account.objects.get(username=username, client_id=client.id) except (Account.DoesNotExist, Account.MultipleObjectsReturned): account = Account(username=username, access_token="", client=client) try: access_token = mastodon.log_in(username, password) account.access_token = access_token account.save() request.session['username'] = username user = mastodon.account_verify_credentials() request.session['user'] = user return redirect(home) except: # FIXME: add the errors return render(request, 'setup/login.html', {'form': form}) else: return render(request, 'setup/login.html', {'form': form})
def __init__( self, mastodon: Mastodon, hihobot: Hihobot, wait_span: float, ): self.mastodon = mastodon self.hihobot = hihobot self.wait_span = wait_span self.me = mastodon.account_verify_credentials()
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)
def fav2(return_type="list", switch=None): wait_sec = 30 start = time.time() e = lambda a, b : round(a-b) print(return_type + " is selected!") Mastodon = login(switch) my_cred = Mastodon.account_verify_credentials() id = my_cred['id'] limit = 40 #40 print("Your id is {0}".format(id)) page = 0 favourites = Mastodon.favourites(None,None,limit) max_id = favourites[-1]['_pagination_next']['max_id'] print("count: {0} ({1})".format(str(len(favourites)), str(len(favourites)))) while True: latest_favourites = Mastodon.favourites(max_id,None,limit) if isinstance(latest_favourites,dict): pprint("Error code 429:{0} wait {1} sec...".format(latest_favourites['error'],wait_sec)) time.sleep(wait_sec) latest_favourites = previous_favourites continue elif len(latest_favourites) < limit: favourites.extend(latest_favourites) page += 1 elapsed_time = time.time() - start print("End fetch your favourites") print("count: {0} time:{1}sec".format(str(len(favourites)), elapsed_time)) break else: max_id = favourites[-1]['_pagination_next']['max_id'] favourites.extend(latest_favourites) page += 1 previous_favourites = latest_favourites print("count: {0} ({1}) time:{2}sec".format(str(len(favourites)), str(len(latest_favourites)), e(time.time(),start))) time.sleep(3) if return_type == "json": filename = str(id) + "_fav" jsoner(favourtes,filename) else: return favourites
def oauth_callback(request): code = request.GET.get('code', '') mastodon = Mastodon(client_id=request.session['client_id'], client_secret=request.session['client_secret'], api_base_url=request.session['instance']) redirect_uri = request.build_absolute_uri(reverse('oauth_callback')) access_token = mastodon.log_in(code=code, redirect_uri=redirect_uri, scopes=['read', 'write', 'follow']) request.session['access_token'] = access_token user = mastodon.account_verify_credentials() request.session['user'] = user return redirect(home)
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()
def get(self, request, *args, **kwargs): config = Config.load() if config.access_token: try: mastodon_client = Mastodon( \ client_id = config.client_id, \ client_secret = config.client_secret, \ access_token = config.access_token, \ api_base_url = 'https://' + config.instance) account_details = mastodon_client.account_verify_credentials() updated = False if config.username != account_details.username: config.username = account_details.username updated = True if config.display_name != account_details.display_name: config.display_name = account_details.display_name updated = True if config.avatar != account_details.avatar: config.avatar = account_details.avatar updated = True if updated: config.save() return JsonResponse({'status': 'ok', 'result': 'updated'}) else: return JsonResponse({ 'status': 'ok', 'result': 'not_modified' }) except MastodonUnauthorizedError as e: reset_access_token(config) config.save() NabMastodond.signal_daemon() return HttpResponse( 'Unauthorized', content= '{{"status":"error","result":"unauthorized","message":"{e}"}}' .format(e=e), mimetype='application/json', status=401) except MastodonError as e: return HttpResponse( 'Unknown error', content='{{"status":"error","message":"{e}"}}'.format(e=e), mimetype='application/json', status=500) else: return HttpResponse( 'Not found', content='{"status":"error","result":"not_found"}', mimetype='application/json', status=404)
def push(id): c = mysql.connection.cursor() c.execute( "SELECT client_id, client_secret, secret FROM credentials WHERE id = (SELECT credentials_id FROM bots WHERE handle = %s)", (id, )) login = c.fetchone() client = Mastodon(client_id=login[0], client_secret=login[1], access_token=login[2], api_base_url="https://{}".format(id.split("@")[2])) c.execute( "SELECT push_private_key, push_secret, replies_enabled FROM bots WHERE handle = %s", (id, )) bot = c.fetchone() if not bot[2]: return "Replies disabled." params = {'privkey': int(bot[0].rstrip("\0")), 'auth': bot[1]} try: push_object = client.push_subscription_decrypt_push( request.data, params, request.headers['Encryption'], request.headers['Crypto-Key']) notification = client.notifications(id=push_object['notification_id']) me = client.account_verify_credentials()['id'] except: return "Push failed - do we still have access to {}?".format(id) # first, check how many times the bot has posted in this thread. # if it's over 15, don't reply. # this is to stop endless reply chains between two bots. try: context = client.status_context(notification['status']['id']) my_posts = 0 for post in context['ancestors']: if post['account']['id'] == me: my_posts += 1 if my_posts >= 15: # don't reply return "Didn't reply." except: # failed to fetch context # assume we haven't been participating in this thread pass functions.make_post([ id, notification['status']['id'], notification['status']['visibility'], "@" + notification['account']['acct'] ]) return "Success!"
class MastodonPublisher(Publisher): def __init__(self, bot, full_config, instance): for i in 'client_id', 'access_token': if not i in instance: raise ValueError( "Mastodon instance missing required configuration item %s: %r" % (i, instance)) for key in 'api_base_url', 'url': url = instance.get(key) if url: break if not url: url = "https://mastodon.social" self.api = Mastodon(client_id=instance['client_id'], client_secret=instance['client_secret'], access_token=instance['access_token'], api_base_url=url) def self_test(self): # Do something that will raise an exception if the credentials are invalid. # Return a string that will let the user know if they somehow gave # credentials to the wrong account. verification = self.api.account_verify_credentials() if 'username' in verification: return ['username'] else: # error raise Exception(repr(verification)) def publish(self, post, publication): media_ids = [] for attachment in post.attachments: if attachment.filename: path = self.attachment_path(attachment.filename) arguments = dict(media_file=path) else: arguments = dict(media_file=attachment.contents, mime_type=attachment.media_type) media = self.api.media_post(**arguments) if media: media_ids.append(media['id']) try: content = publication.content or post.content content = self.mastodon_safe(content) response = self.api.status_post(content, media_ids=media_ids, sensitive=post.sensitive) publication.report_success(response['id']) except Exception, e: publication.report_failure(e)
def profile_add(profile, instance, email, password): """Create a new profile. \b profile: name of the profile to add hostname: instance this account is on email: email to log into the account passwd: password to the account (UNSAFE -- this will be visible)""" if profile is None: profile = input(" Profile name: ") if profile in RESERVED: print_error("Illegal profile name: " + profile) return elif profile in get_known_profiles(): print_error("Profile " + profile + " exists") return instance, client_id, client_secret, token = parse_or_input_profile(profile) if not token: print_error("Could not log you in. Please try again later.\nThis profilename/email will not be saved.") return try: newmasto = Mastodon( client_id=client_id, client_secret=client_secret, access_token=token, api_base_url="https://" + instance) except: print_error("Mastodon error") return # update stuff cfg = get_config() cfg[profile] = { 'instance': instance, 'client_id': client_id, 'client_secret': client_secret, 'token': token } user = newmasto.account_verify_credentials() set_prompt( stylePrompt(user['username'], profile, fg('blue'), fg('cyan')) ) set_active_profile(profile) set_active_mastodon(newmasto) if get_notifications(): kick_new_process( newmasto.user_stream, TootDesktopNotifications(profile) ) cprint(" Profile " + profile + " loaded", fg('green')) save_config() return
class MastodonAccountEditor(AccountEditor): def __init__(self): super().__init__() initialize_mastodon() self.server_url = config("MASTODON_SERVER") mastodon_data_path = Path(__file__).parents[2] / "data" / "mastodon" self.mastodon = Mastodon( client_id=(mastodon_data_path / "my_clientcred.txt").as_posix(), access_token=(mastodon_data_path / "my_usercred.txt").as_posix(), api_base_url=self.server_url) self.name = self.mastodon.account_verify_credentials()['username'] def post_name(self, name): super().post_name(name) self.mastodon.account_update_credentials(display_name=name)
def main(): ''' メインルーチン ''' # Mastodon初期化 mastodon = Mastodon(client_id=CID_FILE, access_token=TOKEN_FILE, api_base_url=URL) # 自分の最新トゥート1件を取得する user_dict = mastodon.account_verify_credentials() user_toots = mastodon.account_statuses(user_dict['id'], limit=1) # トゥートのアプリ名があれば表示する if user_toots[0]['reblog'] is None: print(check_appname(user_toots[0])) # 通常のトゥートの場合 else: print(check_appname(user_toots[0]['reblog'])) # ブーストされたトゥートの場合
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: last_id = account.id with open(last_id_file, 'w+') as f: f.write(str(last_id)) print('Welcoming %s...' % account.username)
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'])