+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
diff --git a/README.md b/README.md
index 675020f..d5b8ff2 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,3 @@
# rct-guest
-rct-guest looks too intense for me!
\ No newline at end of file
+rct-guest looks too intense for me!
diff --git a/login.py b/login.py
new file mode 100755
index 0000000..77deba4
--- /dev/null
+++ b/login.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python3
+# https://github.com/Lynnesbian/fediverse-bot-template
+# Copyright (C) 2019 Lynne (@lynnesbian@fedi.lynnesbian.space)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# import Mastodon.py and other modules
+from mastodon import Mastodon
+import os, json, re, shutil, sys
+
+# specify defaults
+
+cfg = {
+ "site": "https://botsin.space",
+}
+
+# scopes define what your bot has access to.
+# a list of scopes is available here: https://docs.joinmastodon.org/api/permissions/
+# to see what scopes you need for a particular action, check the relevant section in the Mastodon REST API docs.
+# for example, if you want to see what you need to manage statuses, see https://docs.joinmastodon.org/api/rest/statuses/
+# the permissions below allow you to read posts, see who's following you (and who you're following), post statuses, and view notifications (needed for replying to people).
+scopes = ["read:statuses", "read:accounts", "write:statuses", "read:notifications"]
+
+# try to read from the existing config.json. if it's not there, use the defaults to create a new one.
+try:
+ cfg.update(json.load(open('config.json', 'r')))
+except:
+ json.dump(cfg, open("config.json", "w+"))
+
+# load meta information, which defines the bot name, source code URL, etc.
+# note that it is mandatory to provide a source code link, as stated by the AGPLv3 license.
+try:
+ meta = json.load(open("meta.json", 'r'))
+except OSError as e:
+ print("Failed to load meta.json: {}".format(e.strerror))
+ sys.exit(1)
+except:
+ raise
+
+if "client" not in cfg:
+ print("No application info -- registering {} with {}".format(meta['bot_name'], cfg['site']))
+ client_id, client_secret = Mastodon.create_app(meta['bot_name'],
+ api_base_url=cfg['site'],
+ scopes=scopes,
+ website=meta['source_url'])
+
+ # save application information
+ cfg['client'] = {
+ "id": client_id,
+ "secret": client_secret
+ }
+
+if "secret" not in cfg:
+ print("No user credentials -- logging in to {}".format(cfg['site']))
+ client = Mastodon(client_id = cfg['client']['id'],
+ client_secret = cfg['client']['secret'],
+ api_base_url=cfg['site'])
+
+ print("Open this URL and authenticate to give {} access to your bot's account: {}".format(meta['bot_name'], client.auth_request_url(scopes=scopes)))
+ # log in and save the provided information
+ cfg['secret'] = client.log_in(code=input("Secret: "), scopes=scopes)
+
+# save the current configuration
+
+json.dump(cfg, open("config.json", "w+"))
+
+# test login information
+client = Mastodon(
+ client_id=cfg['client']['id'],
+ client_secret = cfg['client']['secret'],
+ access_token=cfg['secret'],
+ api_base_url=cfg['site'])
+
+me = client.account_verify_credentials()
+
+print("Done!")
diff --git a/meta.json b/meta.json
new file mode 100644
index 0000000..994a789
--- /dev/null
+++ b/meta.json
@@ -0,0 +1,5 @@
+{
+ "bot_name": "My cool fedi bot",
+ "source_url": "https://github.com/Lynnesbian/fediverse-bot-template",
+ "admin": "you@your.instance"
+}
diff --git a/post.py b/post.py
new file mode 100644
index 0000000..8ef1e7a
--- /dev/null
+++ b/post.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+# https://github.com/Lynnesbian/fediverse-bot-template
+# Copyright (C) 2019 Lynne (@lynnesbian@fedi.lynnesbian.space)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# import Mastodon.py and other modules
+from mastodon import Mastodon
+import json, random
+
+try:
+ cfg = json.load(open('config.json', 'r'))
+except:
+ print("Couldn't load config.json. Make sure you run main.py first!\n-----")
+ raise
+meta = json.load(open('meta.json', 'r'))
+
+# log in
+client = Mastodon(
+ client_id=cfg['client']['id'],
+ client_secret=cfg['client']['secret'],
+ access_token=cfg['secret'],
+ api_base_url=cfg['site'])
+
+# make a post!
+# if you delete this line, you can also remove the random module from the imports list.
+client.status_post("Hi! Your random number is: {}".format(random.randint(0,10000)))
diff --git a/reply.py b/reply.py
new file mode 100644
index 0000000..5e7488b
--- /dev/null
+++ b/reply.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python3
+# Copyright (C) 2019 Lynne (@lynnesbian@fedi.lynnesbian.space)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+from mastodon import Mastodon, StreamListener
+from bs4 import BeautifulSoup
+
+# use multiprocessing to run multiple reply listener threads at once
+# this means we can handle more than one reply at a time!
+from multiprocessing import Pool
+import os, random, re, json, re, sys
+
+try:
+ cfg = json.load(open('config.json', 'r'))
+except:
+ print("Couldn't load config.json. Make sure you run login.py first!\n-----")
+ raise
+meta = json.load(open('meta.json', 'r'))
+
+print("Logging in...")
+
+client = Mastodon(
+ client_id=cfg['client']['id'],
+ client_secret=cfg['client']['secret'],
+ access_token=cfg['secret'],
+ api_base_url=cfg['site'])
+
+# extract the handle (the @username@instance part)
+handle = "@{}@{}".format(client.account_verify_credentials()['username'], re.match("https://([^/]*)/?", cfg['site']).group(1)).lower()
+
+# convert the text of the status to plain text
+# this part's a bit of a mess!!
+def extract_toot(toot):
+ toot = toot.replace("'", "'") #convert HTML stuff to normal stuff
+ toot = toot.replace(""", '"') #ditto
+ soup = BeautifulSoup(toot, "html.parser")
+ for lb in soup.select("br"): #replace
with linebreak
+ lb.insert_after("\n")
+ lb.decompose()
+
+ for p in soup.select("p"): #ditto for
+ p.insert_after("\n")
+ p.unwrap()
+
+ for ht in soup.select("a.hashtag"): #make hashtags no longer links, just text
+ ht.unwrap()
+
+ for link in soup.select("a"): #convert