Comparar commits
47 commits
Autor | SHA1 | Fecha | |
---|---|---|---|
|
a0ecfec771 | ||
|
b62782e519 | ||
|
6a877e6e46 | ||
|
b7a55dc0dc | ||
|
60da5d7a88 | ||
|
5c439bdf86 | ||
|
f740c5170a | ||
|
469488393a | ||
|
a9f319c958 | ||
|
d152aa584a | ||
|
7057bbd899 | ||
|
9ce0834749 | ||
|
06f43d244d | ||
|
921d642d57 | ||
|
c7363875e9 | ||
|
dc5819821b | ||
|
b6d5ae1421 | ||
|
d7e78f959c | ||
|
f12c26cd8b | ||
|
99762c74b2 | ||
|
5992e67172 | ||
|
c104d250d5 | ||
|
081afcde0d | ||
|
761bea442e | ||
d82ad780d2 | |||
450d412bf7 | |||
f83d45ce0c | |||
bec3925b20 | |||
|
fa047b0593 | ||
|
739c8bfb74 | ||
|
6ac6a05fbc | ||
|
42f0c3e58f | ||
|
4d2da23196 | ||
|
5ba03a1c86 | ||
|
93b3a4334c | ||
|
0ad0003d49 | ||
|
1636e86b6d | ||
|
213bd73cbe | ||
|
3b01828438 | ||
|
e80a59a4ec | ||
|
8f987b48e3 | ||
|
7256cf2a30 | ||
|
80e8815166 | ||
|
95220124f0 | ||
|
c60735b84b | ||
|
9ef90164b0 | ||
|
0b52e0090d |
S'han modificat 12 arxius amb 2203 adicions i 1879 eliminacions
72
README.md
72
README.md
|
@ -1,5 +1,5 @@
|
||||||
# Fediverse Stats
|
# Fediverse Stats
|
||||||
This code gets all peers from running Mastodon, Pleroma and Lemmy host servers and then all peers from host server's peers. Goal is to collect maximum number
|
This code gets all peers from mastodon.social. Goal is to collect maximum number
|
||||||
of alive fediverse's servers and then query their API to obtain their registered users (if their API provide such information).
|
of alive fediverse's servers and then query their API to obtain their registered users (if their API provide such information).
|
||||||
At the end it post the results to host server bot account.
|
At the end it post the results to host server bot account.
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ At the end it post the results to host server bot account.
|
||||||
|
|
||||||
- **Python 3**
|
- **Python 3**
|
||||||
- Postgresql server
|
- Postgresql server
|
||||||
- Mastodon or Pleroma running server.
|
- Mastodon running server.
|
||||||
|
|
||||||
### Usage:
|
### Usage:
|
||||||
|
|
||||||
|
@ -15,67 +15,11 @@ Within Python Virtual Environment:
|
||||||
|
|
||||||
1. Run `pip install -r requirements.txt` to install needed libraries.
|
1. Run `pip install -r requirements.txt` to install needed libraries.
|
||||||
|
|
||||||
2. Run `python db-setup.py` to setup and create new Postgresql database and needed tables in it.
|
2. Run `python fetchservers.py` to add servers to alive servers database.
|
||||||
|
|
||||||
3. Run `python setup.py` to get your bot's access token of your Mastodon or Pleroma server existing account. It will be saved to 'secrets/secrets.txt' for further use.
|
3. Run `python fediverse.py` to query world alive servers API. It gets data from server's nodeinfo.
|
||||||
|
|
||||||
4. Run `python getpeers.py` to get all peers from your host and the whole world of fediverse's servers (or almost the whole world).
|
4. Use your favourite scheduling method to set `python fediverse.py` to run twice daily, `python fetchservers.py` one time daily, `python fediquery.py` to run every minute and `python uptime.py' every minute to publish best fediverse uptime.
|
||||||
|
|
||||||
5. Run `python fetchservers.py` to add servers to alive servers database.
|
|
||||||
|
|
||||||
6. Run `python fediverse.py` to query world alive servers API. It gets data from server's API according this table:
|
|
||||||
|
|
||||||
| Software | API peers | API users (nodeinfo/2.0.json) | API users (nodeinfo/2.0) | API users (api/v1/instance) | API users (main/nodeinfo/2.0) | API users (api/nodeinfo/2.0.json) | API users (api/nodeinfo) | Software |
|
|
||||||
|:--------------:|:---------------------:|:------------------------------------------:|:----------------------------------------------------------------------------------------------:|:---------------------------:|:-----------------------------:|:---------------------------------:|:---------------------------:|:--------------:|
|
|
||||||
| Diaspora | | ['usage']['users']['total'] | | | | | | Diaspora |
|
|
||||||
| Friendica | api/v1/instance/peers | | ['usage']['users']['total'] | | | | | Friendica |
|
|
||||||
| Ganggo | | | ['usage']['users']['total'] | | | | | Ganggo |
|
|
||||||
| GNU Social | | | | | ['usage']['users']['total'] | | | GNU Social |
|
|
||||||
| GNU Social 2.x | | | | | | ['usage']['users']['total'] | | GNU Social 2.x |
|
|
||||||
| Groundpolis | | | ['usage']['users']['total'] | | | | | Groundpolis |
|
|
||||||
| Hubzilla | | | ['usage']['users']['total'] | | | | | Hubzilla |
|
|
||||||
| Lemmy | api/v2/site ['federated_instances']['linked'] | ['usage']['users']['total']| | | | | | Lemmy |
|
|
||||||
| Mastodon | api/v1/instance/peers | ['usage']['users']['total'] (since v3.0.0) | | ['stats']['user_count'] | | | | Mastodon |
|
|
||||||
| Misskey | | | **NO** (['usage']['users'] returns {}) so using ['usage']['users']['activeHalfyear'] instead | | | | | Misskey |
|
|
||||||
| Osada | | | ['usage']['users']['total'] | | | | | Osada |
|
|
||||||
| Peertube | | ['usage']['users']['total'] | | | | | | Peertube |
|
|
||||||
| Pixelfed | | | | | | ['usage']['users']['total'] | | Pixelfed |
|
|
||||||
| Pleroma | api/v1/instance/peers | ['usage']['users']['total'] | | | | | | Pleroma |
|
|
||||||
| Plume | | | ['usage']['users']['total'] | | | | | Plume |
|
|
||||||
| Prismo | | | ['usage']['users']['total'] | | | | | Prismo |
|
|
||||||
| Ravenvale | | ['usage']['users']['total'] | | | | | | Ravenvale |
|
|
||||||
| Squus | | | ['usage']['users']['total'] | | | | | Squus |
|
|
||||||
| Writefreely | | | | | | | ['usage']['users']['total'] | Writefreely |
|
|
||||||
| Zap | | | ['usage']['users']['total'] | | | | | Zap |
|
|
||||||
|
|
||||||
| software | API | software name |
|
|
||||||
|-------------|:---------------------:|:--------------------:|
|
|
||||||
| diaspora | nodeinfo/2.0.json | ['software']['name'] |
|
|
||||||
| dolphin | nodeinfo/2.0 | ['software']['name'] |
|
|
||||||
| ganggo | nodeinfo/2.0 | ['software']['name'] |
|
|
||||||
| friendica | nodeinfo/2.0 | ['software']['name'] |
|
|
||||||
| gnusocial | main/nodeinfo/2.0 | ['software']['name'] |
|
|
||||||
| gnusocialv2 | api/nodeinfo/2.0.json | ['software']['name'] |
|
|
||||||
| groundpolis | nodeinfo/2.0 | ['software']['name'] |
|
|
||||||
| hubzilla | nodeinfo/2.0 | ['software']['name'] |
|
|
||||||
| lemmy | nodeinfo/2.0.json | ['software']['name'] |
|
|
||||||
| mastodon | nodeinfo/2.0.json | ['software']['name'] |
|
|
||||||
| misskey | nodeinfo/2.0 | ['software']['name'] |
|
|
||||||
| osada | nodeinfo/2.0.json | ['software']['name'] |
|
|
||||||
| peertube | nodeinfo/2.0.json | ['software']['name'] |
|
|
||||||
| pixelfed | api/nodeinfo/2.0.json | ['software']['name'] |
|
|
||||||
| pleroma | nodeinfo/2.0.json | ['software']['name'] |
|
|
||||||
| plume | nodeinfo/2.0 | ['software']['name'] |
|
|
||||||
| prismo | nodeinfo/2.0.json | ['software']['name'] |
|
|
||||||
| ravenvale | nodeinfo/2.0.json | ['software']['name'] |
|
|
||||||
| squs | nodeinfo/2.0 | ['software']['name'] |
|
|
||||||
| wordpress | wp-json/nodeinfo/2.0 | ['software']['name'] |
|
|
||||||
| writefreely | api/nodeinfo | ['software']['name'] |
|
|
||||||
| zap | nodeinfo/2.0.json | ['software']['name'] |
|
|
||||||
|
|
||||||
7. Run `python uptime_setup.py` to get your Uptime bot's access token of your Mastodon or Pleroma server existing account. It will be saved to 'secrets/uptime_secrets.txt' for further use.
|
|
||||||
|
|
||||||
8. Use your favourite scheduling method to set `python fediverse.py` to run twice daily, `python fetchservers.py` one time daily, `python getworld.py` to run monthly and `python uptime.py` (choose your desired frequency) if you want to publish best fediverse's servers uptime.
|
|
||||||
|
|
||||||
18.2.2021 - New feature! Added [Lemmy project](https://join.lemmy.ml)
|
18.2.2021 - New feature! Added [Lemmy project](https://join.lemmy.ml)
|
||||||
12.5.2021 - New feature! Added Wordpress support. The code can now detect Wordpress instances with ActivityPub enabled plugin.
|
12.5.2021 - New feature! Added Wordpress support. The code can now detect Wordpress instances with ActivityPub enabled plugin.
|
||||||
|
@ -83,3 +27,9 @@ Within Python Virtual Environment:
|
||||||
21.8.2021 - New feature! Added Best Fediverse's servers Uptime publishing bot.
|
21.8.2021 - New feature! Added Best Fediverse's servers Uptime publishing bot.
|
||||||
22.10.2021 - New feature! Added [Funkwhale](https://funkwhale.audio) support.
|
22.10.2021 - New feature! Added [Funkwhale](https://funkwhale.audio) support.
|
||||||
26.10.2021 - New feature! Added [Socialhome](https://socialhome.network) support.
|
26.10.2021 - New feature! Added [Socialhome](https://socialhome.network) support.
|
||||||
|
2.3.2022 - Improved server nodeinfo detection.
|
||||||
|
4.1.2023 - Refactored.
|
||||||
|
4.1.2023 - Now peers are obtained from mastodon.social's peers list.
|
||||||
|
5.1.2023 - Added fediquery.py. Allow ask the main bot fediverse about `soft` and `server` and it replies to the asking user with its data if any.
|
||||||
|
7.1.2023 - When querying a not found server, we will be added to database if it's alive.
|
||||||
|
8.1.2023 - Save MAU to database.
|
||||||
|
|
1083
database.py
Normal file
1083
database.py
Normal file
La diferencia del archivo ha sido suprimido porque es demasiado grande
Cargar Diff
177
db-setup.py
177
db-setup.py
|
@ -1,177 +0,0 @@
|
||||||
import getpass
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from mastodon import Mastodon
|
|
||||||
from mastodon.Mastodon import MastodonMalformedEventError, MastodonNetworkError, MastodonReadTimeout, MastodonAPIError
|
|
||||||
import psycopg2
|
|
||||||
from psycopg2 import sql
|
|
||||||
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT
|
|
||||||
|
|
||||||
def get_parameter( parameter, file_path ):
|
|
||||||
# Check if secrets file exists
|
|
||||||
if not os.path.isfile(file_path):
|
|
||||||
print("File %s not found, asking."%file_path)
|
|
||||||
write_parameter( parameter, file_path )
|
|
||||||
#sys.exit(0)
|
|
||||||
|
|
||||||
# Find parameter in file
|
|
||||||
with open( file_path ) as f:
|
|
||||||
for line in f:
|
|
||||||
if line.startswith( parameter ):
|
|
||||||
return line.replace(parameter + ":", "").strip()
|
|
||||||
|
|
||||||
# Cannot find parameter, exit
|
|
||||||
print(file_path + " Missing parameter %s "%parameter)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
def write_parameter( parameter, file_path ):
|
|
||||||
|
|
||||||
if not os.path.exists('config'):
|
|
||||||
os.makedirs('config')
|
|
||||||
print("Setting up fediverse parameters...")
|
|
||||||
print("\n")
|
|
||||||
fediverse_db = input("fediverse db name: ")
|
|
||||||
fediverse_db_user = input("fediverse db user: ")
|
|
||||||
mastodon_db = input("Mastodon db name: ")
|
|
||||||
mastodon_db_user = input("Mastodon db user: ")
|
|
||||||
|
|
||||||
with open(file_path, "w") as text_file:
|
|
||||||
|
|
||||||
print("fediverse_db: {}".format(fediverse_db), file=text_file)
|
|
||||||
print("fediverse_db_user: {}".format(fediverse_db_user), file=text_file)
|
|
||||||
print("mastodon_db: {}".format(mastodon_db), file=text_file)
|
|
||||||
print("mastodon_db_user: {}".format(mastodon_db_user), file=text_file)
|
|
||||||
|
|
||||||
def create_table(db, db_user, table, sql):
|
|
||||||
|
|
||||||
conn = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
conn = psycopg2.connect(database = db, user = db_user, password = "", host = "/var/run/postgresql", port = "5432")
|
|
||||||
cur = conn.cursor()
|
|
||||||
|
|
||||||
print("Creating table.. "+table)
|
|
||||||
# Create the table in PostgreSQL database
|
|
||||||
cur.execute(sql)
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
|
|
||||||
print("Table "+table+" created!")
|
|
||||||
print("\n")
|
|
||||||
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
|
|
||||||
if conn is not None:
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# main
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
|
||||||
# Load configuration from config file
|
|
||||||
config_filepath = "config/db_config.txt"
|
|
||||||
fediverse_db = get_parameter("fediverse_db", config_filepath)
|
|
||||||
fediverse_db_user = get_parameter("fediverse_db_user", config_filepath)
|
|
||||||
mastodon_db = get_parameter("mastodon_db", config_filepath)
|
|
||||||
mastodon_db_user = get_parameter("mastodon_db_user", config_filepath)
|
|
||||||
|
|
||||||
############################################################
|
|
||||||
# create database
|
|
||||||
############################################################
|
|
||||||
|
|
||||||
conn = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
conn = psycopg2.connect(dbname='postgres',
|
|
||||||
user=fediverse_db_user, host='',
|
|
||||||
password='')
|
|
||||||
|
|
||||||
conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
|
|
||||||
|
|
||||||
cur = conn.cursor()
|
|
||||||
|
|
||||||
print("Creating database " + fediverse_db + ". Please wait...")
|
|
||||||
|
|
||||||
cur.execute(sql.SQL("CREATE DATABASE {}").format(
|
|
||||||
sql.Identifier(fediverse_db))
|
|
||||||
)
|
|
||||||
print("Database " + fediverse_db + " created!")
|
|
||||||
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
|
|
||||||
if conn is not None:
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
#############################################################################################
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
conn = None
|
|
||||||
conn = psycopg2.connect(database = fediverse_db, user = fediverse_db_user, password = "", host = "/var/run/postgresql", port = "5432")
|
|
||||||
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
|
|
||||||
print(error)
|
|
||||||
# Load configuration from config file
|
|
||||||
os.remove("db_config.txt")
|
|
||||||
print("Exiting. Run db-setup again with right parameters")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
if conn is not None:
|
|
||||||
|
|
||||||
print("\n")
|
|
||||||
print("fediverse parameters saved to db-config.txt!")
|
|
||||||
print("\n")
|
|
||||||
|
|
||||||
############################################################
|
|
||||||
# Create needed tables
|
|
||||||
############################################################
|
|
||||||
|
|
||||||
print("Creating table...")
|
|
||||||
|
|
||||||
########################################
|
|
||||||
|
|
||||||
db = fediverse_db
|
|
||||||
db_user = fediverse_db_user
|
|
||||||
table = "world"
|
|
||||||
sql = "create table "+table+" (server varchar(200) PRIMARY KEY, federated_with varchar(200), updated_at timestamptz, saved_at timestamptz, checked boolean)"
|
|
||||||
create_table(db, db_user, table, sql)
|
|
||||||
|
|
||||||
table = "fediverse"
|
|
||||||
sql = "create table "+table+" (server varchar(200) PRIMARY KEY, users INT, updated_at timestamptz, software varchar(50), version varchar(100), first_checked_at timestamptz, last_checked_at timestamptz, downs int)"
|
|
||||||
create_table(db, db_user, table, sql)
|
|
||||||
|
|
||||||
table = "totals"
|
|
||||||
sql = "create table "+table+" (datetime timestamptz PRIMARY KEY, total_servers INT, total_users INT)"
|
|
||||||
create_table(db, db_user, table, sql)
|
|
||||||
|
|
||||||
table = "evo"
|
|
||||||
sql = "create table "+table+" (datetime timestamptz PRIMARY KEY, servers INT, users INT)"
|
|
||||||
create_table(db, db_user, table, sql)
|
|
||||||
|
|
||||||
table = "closed"
|
|
||||||
sql = "create table "+table+" (server varchar(200) PRIMARY KEY, software varchar(10), closed boolean)"
|
|
||||||
create_table(db, db_user, table, sql)
|
|
||||||
|
|
||||||
table = "botreplies"
|
|
||||||
sql = "create table "+table+" (status_id bigint PRIMARY KEY, query_user varchar(40), status_created_at timestamptz)"
|
|
||||||
create_table(db, db_user, table, sql)
|
|
||||||
|
|
||||||
#####################################
|
|
||||||
|
|
||||||
print("Done!")
|
|
||||||
print("Now you can run setup.py!")
|
|
||||||
print("\n")
|
|
196
fediquery.py
Normal file
196
fediquery.py
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import re
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from setup import Setup
|
||||||
|
from mastodon import Mastodon
|
||||||
|
from database import Database
|
||||||
|
from query import Query
|
||||||
|
import pdb
|
||||||
|
|
||||||
|
def cleanhtml(raw_html):
|
||||||
|
cleanr = re.compile('<.*?>')
|
||||||
|
cleantext = re.sub(cleanr, '', raw_html)
|
||||||
|
return cleantext
|
||||||
|
|
||||||
|
def unescape(s):
|
||||||
|
s = s.replace("'", "'")
|
||||||
|
return s
|
||||||
|
|
||||||
|
def replying():
|
||||||
|
|
||||||
|
reply = False
|
||||||
|
|
||||||
|
content = cleanhtml(text)
|
||||||
|
content = unescape(content)
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
start = content.index("@")
|
||||||
|
end = content.index(" ")
|
||||||
|
if len(content) > end:
|
||||||
|
|
||||||
|
content = content[0: start:] + content[end +1::]
|
||||||
|
|
||||||
|
neteja = content.count('@')
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while i < neteja :
|
||||||
|
|
||||||
|
start = content.rfind("@")
|
||||||
|
end = len(content)
|
||||||
|
content = content[0: start:] + content[end +1::]
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
question = content.lower()
|
||||||
|
|
||||||
|
query_word = question
|
||||||
|
query_word_length = len(query_word)
|
||||||
|
|
||||||
|
if query_word[:4] == 'soft':
|
||||||
|
|
||||||
|
reply = True
|
||||||
|
|
||||||
|
if query_word[:6] == 'server':
|
||||||
|
|
||||||
|
reply = True
|
||||||
|
|
||||||
|
if query_word[:3] == 'mau':
|
||||||
|
|
||||||
|
reply = True
|
||||||
|
|
||||||
|
return (reply, query_word)
|
||||||
|
|
||||||
|
except ValueError as v_error:
|
||||||
|
|
||||||
|
print(v_error)
|
||||||
|
|
||||||
|
query_word = ''
|
||||||
|
|
||||||
|
return (reply, query_word)
|
||||||
|
|
||||||
|
# main
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
setup = Setup()
|
||||||
|
|
||||||
|
mastodon = Mastodon(
|
||||||
|
access_token = setup.mastodon_app_token,
|
||||||
|
api_base_url= setup.mastodon_hostname
|
||||||
|
)
|
||||||
|
|
||||||
|
db = Database()
|
||||||
|
|
||||||
|
query = Query()
|
||||||
|
|
||||||
|
now = datetime.now()
|
||||||
|
|
||||||
|
bot_id = mastodon.me().id
|
||||||
|
|
||||||
|
notifications = mastodon.notifications()
|
||||||
|
|
||||||
|
if len(notifications) == 0:
|
||||||
|
|
||||||
|
print('No mentions')
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
for notif in notifications:
|
||||||
|
|
||||||
|
notification_id = notif.id
|
||||||
|
|
||||||
|
if notif.type != 'mention':
|
||||||
|
|
||||||
|
print(f'dismissing notification {notification_id}')
|
||||||
|
|
||||||
|
mastodon.notifications_dismiss(notification_id)
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
account_id = notif.account.id
|
||||||
|
|
||||||
|
username = notif.account.acct
|
||||||
|
|
||||||
|
status_id = notif.status.id
|
||||||
|
|
||||||
|
text = notif.status.content
|
||||||
|
|
||||||
|
visibility = notif.status.visibility
|
||||||
|
|
||||||
|
reply, query_word = replying()
|
||||||
|
|
||||||
|
if reply == True:
|
||||||
|
|
||||||
|
if query_word[:4] == 'soft':
|
||||||
|
|
||||||
|
key_word = query_word[:4]
|
||||||
|
|
||||||
|
search_soft = query_word[5:]
|
||||||
|
|
||||||
|
if search_soft != '':
|
||||||
|
|
||||||
|
servers, users, mau = db.get_soft_data(search_soft)
|
||||||
|
|
||||||
|
toot_text = f'@{username}, my data for {search_soft} software:\n\n'
|
||||||
|
|
||||||
|
if servers != 0:
|
||||||
|
|
||||||
|
toot_text += f'software :{search_soft}:\nservers: {servers:,}\nusers: {users:,}\nMAU: {mau:,}'
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
toot_text += 'software not found!'
|
||||||
|
|
||||||
|
mastodon.status_post(toot_text, in_reply_to_id=status_id,visibility=visibility)
|
||||||
|
|
||||||
|
print(f'Notification {notification_id} replied')
|
||||||
|
|
||||||
|
mastodon.notifications_dismiss(notification_id)
|
||||||
|
|
||||||
|
if query_word[:6] == 'server':
|
||||||
|
|
||||||
|
key_word = query_word[:6]
|
||||||
|
|
||||||
|
search_server = query_word[7:]
|
||||||
|
|
||||||
|
if search_server != '':
|
||||||
|
|
||||||
|
server, software, version, users, mau, alive = db.fediquery_server_data(search_server)
|
||||||
|
|
||||||
|
toot_text = f'@{username}, my data for {search_server}:\n\n'
|
||||||
|
|
||||||
|
if server == '' or server != '' and not alive:
|
||||||
|
|
||||||
|
server, software, version, users, mau, alive = query.getsoft(search_server)
|
||||||
|
|
||||||
|
if server != '' and alive:
|
||||||
|
|
||||||
|
toot_text += f"\nServer not found but it's alive. Added!\n\n"
|
||||||
|
|
||||||
|
if alive:
|
||||||
|
|
||||||
|
toot_text += f'server: {server}\nsoftware: :{software}:\nversion: {version}\nMAU: {int(mau):,}\nusers: {int(users):,}\nalive: {alive}'
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
toot_text += 'server not found!'
|
||||||
|
|
||||||
|
mastodon.status_post(toot_text, in_reply_to_id=status_id,visibility=visibility)
|
||||||
|
|
||||||
|
print(f'Notification {notification_id} replied')
|
||||||
|
|
||||||
|
mastodon.notifications_dismiss(notification_id)
|
||||||
|
else:
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
print(f'Dismissing notification {notification_id}')
|
||||||
|
|
||||||
|
mastodon.notifications_dismiss(notification_id)
|
||||||
|
|
||||||
|
except MastodonNotFoundError as notfound_error:
|
||||||
|
|
||||||
|
print(f'{notfound_error}')
|
||||||
|
|
||||||
|
continue
|
967
fediverse.py
967
fediverse.py
La diferencia del archivo ha sido suprimido porque es demasiado grande
Cargar Diff
487
fetchservers.py
487
fetchservers.py
|
@ -4,33 +4,36 @@ import os
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
import os.path
|
import os.path
|
||||||
import psycopg2
|
from setup import Setup
|
||||||
from multiprocessing import Pool, Manager
|
from database import Database
|
||||||
import aiohttp
|
import requests
|
||||||
import asyncio
|
import urllib3
|
||||||
import socket
|
import socket
|
||||||
|
import ray
|
||||||
|
import pdb
|
||||||
|
|
||||||
apis = ['/nodeinfo/2.0?',
|
ray.init(num_cpus = 25) # Specify this system CPUs.
|
||||||
'/nodeinfo/2.0.json?',
|
|
||||||
'/main/nodeinfo/2.0?',
|
|
||||||
'/api/statusnet/config?',
|
|
||||||
'/api/nodeinfo/2.0.json?',
|
|
||||||
'/api/nodeinfo?',
|
|
||||||
'/api/v1/instance?',
|
|
||||||
'/wp-json/nodeinfo/2.0?',
|
|
||||||
'/api/v1/instance/nodeinfo/2.0?',
|
|
||||||
'/.well-known/x-nodeinfo2?'
|
|
||||||
]
|
|
||||||
|
|
||||||
client_exceptions = (
|
from ray.exceptions import (
|
||||||
aiohttp.ClientResponseError,
|
RaySystemError,
|
||||||
aiohttp.ClientConnectionError,
|
RayError,
|
||||||
aiohttp.ClientConnectorError,
|
RayTaskError,
|
||||||
aiohttp.ClientError,
|
ObjectStoreFullError,
|
||||||
asyncio.TimeoutError,
|
|
||||||
socket.gaierror,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
apis = ['/api/v1/instance',
|
||||||
|
'/api/v1/nodeinfo',
|
||||||
|
'/nodeinfo/2.0',
|
||||||
|
'/nodeinfo/2.0.json',
|
||||||
|
'/nodeinfo/2.1.json',
|
||||||
|
'/main/nodeinfo/2.0',
|
||||||
|
'/api/statusnet/config',
|
||||||
|
'/api/nodeinfo/2.0.json',
|
||||||
|
'/api/nodeinfo',
|
||||||
|
'/wp-json/nodeinfo/2.0',
|
||||||
|
'/api/v1/instance/nodeinfo/2.0',
|
||||||
|
'/.well-known/x-nodeinfo2'
|
||||||
|
]
|
||||||
|
|
||||||
def is_json(myjson):
|
def is_json(myjson):
|
||||||
|
|
||||||
|
@ -40,124 +43,8 @@ def is_json(myjson):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def write_api(server, software, users, alive, api, soft_version):
|
@ray.remote
|
||||||
|
def getsoft(server):
|
||||||
insert_sql = "INSERT INTO fediverse(server, updated_at, software, users, alive, users_api, version) VALUES(%s,%s,%s,%s,%s,%s,%s) ON CONFLICT DO NOTHING"
|
|
||||||
|
|
||||||
conn = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
conn = psycopg2.connect(database=fediverse_db, user=fediverse_db_user, password="", host="/var/run/postgresql",
|
|
||||||
port="5432")
|
|
||||||
|
|
||||||
cur = conn.cursor()
|
|
||||||
|
|
||||||
cur.execute(insert_sql, (server, now, software, users, alive, api, soft_version))
|
|
||||||
|
|
||||||
cur.execute(
|
|
||||||
"UPDATE fediverse SET updated_at=(%s), software=(%s), users=(%s), alive=(%s), users_api=(%s), version=(%s) where server=(%s)",
|
|
||||||
(now, software, users, alive, api, soft_version, server)
|
|
||||||
)
|
|
||||||
|
|
||||||
cur.execute("UPDATE world SET checked='t' where server=(%s)", (server,))
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
|
|
||||||
cur.close()
|
|
||||||
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
|
|
||||||
if conn is not None:
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
async def getsoft(server):
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
socket.gethostbyname(server)
|
|
||||||
|
|
||||||
except socket.gaierror:
|
|
||||||
|
|
||||||
pass
|
|
||||||
return
|
|
||||||
|
|
||||||
soft = ''
|
|
||||||
|
|
||||||
url = 'https://' + server
|
|
||||||
|
|
||||||
user_agent = {'User-agent': 'Mozilla/5.0'}
|
|
||||||
|
|
||||||
timeout = aiohttp.ClientTimeout(total=3)
|
|
||||||
async with aiohttp.ClientSession(timeout=timeout, headers=user_agent) as session:
|
|
||||||
for api in apis:
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
async with session.get(url + api) as response:
|
|
||||||
if response.status == 200:
|
|
||||||
try:
|
|
||||||
response_json = await response.json()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
except aiohttp.ClientConnectorError as err:
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
if response.status == 200 and api != '/api/v1/instance?':
|
|
||||||
|
|
||||||
if api != '/.well-known/x-nodeinfo2?':
|
|
||||||
|
|
||||||
try:
|
|
||||||
soft = response_json['software']['name']
|
|
||||||
soft = soft.lower()
|
|
||||||
soft_version = response_json['software']['version']
|
|
||||||
users = response_json['usage']['users']['total']
|
|
||||||
if users > 1000000:
|
|
||||||
return
|
|
||||||
alive = True
|
|
||||||
write_api(server, soft, users, alive, api, soft_version)
|
|
||||||
print("Server " + server + " (" + soft + " " + soft_version + ") is alive!")
|
|
||||||
return
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
|
|
||||||
try:
|
|
||||||
soft = response_json['server']['software']
|
|
||||||
soft = soft.lower()
|
|
||||||
soft_version = response_json['server']['version']
|
|
||||||
users = response_json['usage']['users']['total']
|
|
||||||
if users > 1000000:
|
|
||||||
return
|
|
||||||
alive = True
|
|
||||||
if soft == 'socialhome':
|
|
||||||
write_api(server, soft, users, alive, api, soft_version)
|
|
||||||
print("Server " + server + " (" + soft + " " + soft_version + ") is alive!")
|
|
||||||
return
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if response.status == 200 and soft == '' and api == "/api/v1/instance?":
|
|
||||||
soft = 'mastodon'
|
|
||||||
users = response_json['stats']['user_count']
|
|
||||||
soft_version = response_json['version']
|
|
||||||
if users > 1000000:
|
|
||||||
return
|
|
||||||
alive = True
|
|
||||||
write_api(server, soft, users, alive, api)
|
|
||||||
print("Server " + server + " (" + soft + ") is alive!")
|
|
||||||
|
|
||||||
def getserver(server, x):
|
|
||||||
|
|
||||||
server = server[0].rstrip('.').lower()
|
|
||||||
|
|
||||||
if server.find(".") == -1:
|
if server.find(".") == -1:
|
||||||
return
|
return
|
||||||
|
@ -168,105 +55,281 @@ def getserver(server, x):
|
||||||
if server.find(":") != -1:
|
if server.find(":") != -1:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
soft = ''
|
||||||
|
|
||||||
|
is_nodeinfo = False
|
||||||
|
|
||||||
|
url = 'https://' + server
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
response = requests.get(url + '/.well-known/nodeinfo', headers = setup.user_agent, timeout=3)
|
||||||
coroutines = [getsoft(server)]
|
|
||||||
soft = loop.run_until_complete(asyncio.gather(*coroutines, return_exceptions=True))
|
|
||||||
|
|
||||||
except:
|
if response.status_code == 200:
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
response_json = response.json()
|
||||||
|
|
||||||
|
if len(response_json['links']) == 1:
|
||||||
|
|
||||||
|
nodeinfo = response_json['links'][0]['href'].replace(f'https://{server}','')
|
||||||
|
|
||||||
|
elif len(response_json['links']) == 2:
|
||||||
|
|
||||||
|
nodeinfo = response_json['links'][1]['href'].replace(f'https://{server}','')
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
nodeinfo_data = requests.get(url + nodeinfo, headers = setup.user_agent, timeout=3)
|
||||||
|
|
||||||
|
if nodeinfo_data.status_code == 200:
|
||||||
|
|
||||||
|
nodeinfo_json = nodeinfo_data.json()
|
||||||
|
|
||||||
|
is_nodeinfo = True
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
print(f"{nodeinfo} not responding: error code {nodeinfo_data.status_code}")
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
print(f'{server} is not responding: error code {response.status_code}')
|
||||||
|
print('*********************************************************************')
|
||||||
|
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
|
||||||
|
for api in apis:
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
response = requests.get(url + api, headers = setup.user_agent, timeout=3)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
|
||||||
|
if is_json(response.text):
|
||||||
|
|
||||||
|
nodeinfo_json = response.json()
|
||||||
|
|
||||||
|
if 'software' in nodeinfo_json:
|
||||||
|
|
||||||
|
nodeinfo = api
|
||||||
|
|
||||||
|
is_nodeinfo = True
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
elif 'title' in nodeinfo_json:
|
||||||
|
|
||||||
|
if nodeinfo_json['title'] == 'Zap':
|
||||||
|
|
||||||
|
nodeinfo = api
|
||||||
|
|
||||||
|
is_nodeinfo = True
|
||||||
|
|
||||||
|
soft = 'zap'
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
elif 'version' in nodeinfo_json:
|
||||||
|
|
||||||
|
nodeinfo = api
|
||||||
|
|
||||||
|
is_nodeinfo = True
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
except requests.exceptions.SSLError as errssl:
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_parameter(parameter, file_path):
|
except requests.exceptions.HTTPError as errh:
|
||||||
# Check if secrets file exists
|
|
||||||
if not os.path.isfile(file_path):
|
|
||||||
print("File %s not found, exiting." % file_path)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# Find parameter in file
|
pass
|
||||||
with open(file_path) as f:
|
|
||||||
for line in f:
|
|
||||||
if line.startswith(parameter):
|
|
||||||
return line.replace(parameter + ":", "").strip()
|
|
||||||
|
|
||||||
# Cannot find parameter, exit
|
except requests.exceptions.ConnectionError as errc:
|
||||||
print(file_path + " Missing parameter %s " % parameter)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
def get_config():
|
except requests.exceptions.ReadTimeout as to_err:
|
||||||
|
|
||||||
# Load configuration from config file
|
pass
|
||||||
config_filepath = "config/config.txt"
|
|
||||||
mastodon_hostname = get_parameter("mastodon_hostname", config_filepath)
|
|
||||||
return mastodon_hostname
|
|
||||||
|
|
||||||
def get_db_config():
|
except requests.exceptions.TooManyRedirects as tmr_err:
|
||||||
|
|
||||||
# Load database config from db_config file
|
pass
|
||||||
db_config_filepath = "config/db_config.txt"
|
|
||||||
fediverse_db = get_parameter("fediverse_db", db_config_filepath)
|
except urllib3.exceptions.LocationParseError as lp_err:
|
||||||
fediverse_db_user = get_parameter("fediverse_db_user", db_config_filepath)
|
|
||||||
return (fediverse_db, fediverse_db_user)
|
pass
|
||||||
|
|
||||||
|
except requests.exceptions.InvalidURL as iu_err:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
except requests.exceptions.ChunkedEncodingError as chunk_err:
|
||||||
|
|
||||||
|
print(f'ChunkedEncodingError! {server}')
|
||||||
|
pass
|
||||||
|
|
||||||
|
except ray.exceptions.RaySystemError as ray_sys_error:
|
||||||
|
|
||||||
|
print(ray_sys_error)
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
if is_nodeinfo:
|
||||||
|
|
||||||
|
if nodeinfo != '/api/v1/instance?':
|
||||||
|
|
||||||
|
if nodeinfo != '/.well-known/x-nodeinfo2?':
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
soft = nodeinfo_json['software']['name']
|
||||||
|
soft = soft.lower()
|
||||||
|
soft_version = nodeinfo_json['software']['version']
|
||||||
|
users = nodeinfo_json.get('usage').get('users').get('total') or '0'
|
||||||
|
if int(users) > 1000000:
|
||||||
|
return
|
||||||
|
self.mau = nodeinfo_json.get('usage').get('users').get('activeMonth') or 0
|
||||||
|
alive = True
|
||||||
|
|
||||||
|
db.write_api(server, soft, users, mau, alive, nodeinfo, soft_version)
|
||||||
|
|
||||||
|
print(f"Server {server} ({soft} {soft_version}) is alive!")
|
||||||
|
print('*********************************************************************')
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
soft = nodeinfo_json['server']['software']
|
||||||
|
soft = soft.lower()
|
||||||
|
soft_version = nodeinfo_json['server']['version']
|
||||||
|
users = nodeinfo_json['usage']['users']['total']
|
||||||
|
if int(users) > 1000000:
|
||||||
|
return
|
||||||
|
self.mau = nodeinfo_json.get('usage').get('users').get('activeMonth') or 0
|
||||||
|
alive = True
|
||||||
|
|
||||||
|
if soft == 'socialhome':
|
||||||
|
|
||||||
|
db.write_api(server, soft, users, mau, alive, nodeinfo, soft_version)
|
||||||
|
|
||||||
|
print('*********************************************************************')
|
||||||
|
print(f"Server {serve}r ({soft} {soft_version}) is alive!")
|
||||||
|
print('*********************************************************************')
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
if soft == '' and nodeinfo == "/api/v1/instance?":
|
||||||
|
|
||||||
|
soft = 'mastodon'
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
users = nodeinfo_json['stats']['user_count']
|
||||||
|
|
||||||
|
if int(users) > 1000000:
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
users = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
soft_version = nodeinfo_json['version']
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
soft_version = 'unknown'
|
||||||
|
|
||||||
|
mau = 0
|
||||||
|
alive = True
|
||||||
|
|
||||||
|
db.write_api(server, soft, users, mau, alive, nodeinfo, soft_version)
|
||||||
|
|
||||||
|
print('*********************************************************************')
|
||||||
|
print(f"Server {server} ({soft}) is alive!")
|
||||||
|
|
||||||
|
elif soft == 'zap' and nodeinfo == "/api/v1/instance?":
|
||||||
|
|
||||||
|
soft = 'zap'
|
||||||
|
users = nodeinfo_json['stats']['user_count']
|
||||||
|
soft_version = nodeinfo_json['version']
|
||||||
|
alive = True
|
||||||
|
|
||||||
|
print(server, soft, users, alive, api)
|
||||||
|
|
||||||
|
print('*********************************************************************')
|
||||||
|
print(f"Server {server} ({soft}) is alive!")
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
print(f'Server {server} is dead')
|
||||||
|
print('*********************************************************************')
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# main
|
# main
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
now = datetime.now()
|
## name: fetchservers.py
|
||||||
start_time = time.time()
|
|
||||||
|
|
||||||
mastodon_hostname = get_config()
|
setup = Setup()
|
||||||
|
|
||||||
fediverse_db, fediverse_db_user = get_db_config()
|
db = Database()
|
||||||
|
|
||||||
world_servers = []
|
res = requests.get('https://' + 'mastodon.social' + setup.peers_api, headers = setup.user_agent, timeout=3)
|
||||||
|
|
||||||
|
hostname_peers = res.json()
|
||||||
|
|
||||||
|
start = datetime.now()
|
||||||
|
|
||||||
|
program = 'fetchservers'
|
||||||
|
|
||||||
|
finish = start
|
||||||
|
|
||||||
|
db.save_time(program, start, finish)
|
||||||
|
|
||||||
|
now = start
|
||||||
|
|
||||||
|
ray_start = time.time()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
conn = None
|
results = ray.get([getsoft.remote(server) for server in hostname_peers])
|
||||||
|
|
||||||
conn = psycopg2.connect(database=fediverse_db, user=fediverse_db_user, password="", host="/var/run/postgresql", port="5432")
|
print(f"duration = {time.time() - ray_start}.\nprocessed servers: {len(results)}")
|
||||||
|
|
||||||
cur = conn.cursor()
|
except:
|
||||||
|
|
||||||
# get world servers list
|
pass
|
||||||
|
|
||||||
cur.execute("select server from world where checked='f'")
|
finish = datetime.now()
|
||||||
|
|
||||||
rows = cur.fetchall()
|
db.save_time(program, start, finish)
|
||||||
|
|
||||||
for row in rows:
|
|
||||||
|
|
||||||
world_servers.append(row[0])
|
|
||||||
|
|
||||||
cur.close()
|
|
||||||
|
|
||||||
print("Remaining servers: " + str(len(world_servers)))
|
|
||||||
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
|
|
||||||
if conn is not None:
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
###########################################################################
|
|
||||||
# multiprocessing!
|
|
||||||
|
|
||||||
m = Manager()
|
|
||||||
q = m.Queue()
|
|
||||||
z = zip(world_servers)
|
|
||||||
|
|
||||||
serv_number = len(world_servers)
|
|
||||||
|
|
||||||
pool_tuple = [(x, q) for x in z]
|
|
||||||
with Pool(processes=64) as pool:
|
|
||||||
pool.starmap(getserver, pool_tuple)
|
|
||||||
|
|
||||||
print('Done.')
|
|
||||||
|
|
156
getpeers.py
156
getpeers.py
|
@ -1,156 +0,0 @@
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
from datetime import datetime
|
|
||||||
import requests
|
|
||||||
import threading
|
|
||||||
import json
|
|
||||||
import psycopg2
|
|
||||||
import pdb
|
|
||||||
|
|
||||||
def write_servers(world_servers):
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
|
|
||||||
while i < len(world_servers):
|
|
||||||
|
|
||||||
saved_at = datetime.now()
|
|
||||||
|
|
||||||
insert_sql = "INSERT INTO world(server, federated_with, updated_at, saved_at) VALUES(%s,%s,%s,%s) ON CONFLICT DO NOTHING"
|
|
||||||
|
|
||||||
conn = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
conn = psycopg2.connect(database = fediverse_db, user = fediverse_db_user, password = "", host = "/var/run/postgresql", port = "5432")
|
|
||||||
|
|
||||||
cur = conn.cursor()
|
|
||||||
|
|
||||||
cur.execute(insert_sql, (world_servers[i], peer, updated_at, saved_at,))
|
|
||||||
|
|
||||||
print('writing to database ' + str(i) + ' of ' + str(len(world_servers)) + ': ' + world_servers[i])
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
|
|
||||||
cur.close()
|
|
||||||
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
|
|
||||||
if conn is not None:
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
def get_peers(peer):
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
user_agent = {'User-agent': 'Mozilla/5.0'}
|
|
||||||
|
|
||||||
response = requests.get('https://' + peer + peers_api, headers = user_agent, timeout=3)
|
|
||||||
|
|
||||||
response_json = response.json()
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
print("Server: " + peer + ", " + "federated with " + str(len(response_json)) + " servers")
|
|
||||||
|
|
||||||
for peer_peer in response_json:
|
|
||||||
|
|
||||||
exist_count = world_peers.count(peer_peer)
|
|
||||||
|
|
||||||
if exist_count == 0:
|
|
||||||
|
|
||||||
world_peers.append(peer_peer)
|
|
||||||
except:
|
|
||||||
|
|
||||||
pass
|
|
||||||
except:
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
class Peerthreads(threading.Thread):
|
|
||||||
def __init__(self, peer, peers_semaphore):
|
|
||||||
threading.Thread.__init__(self)
|
|
||||||
self.sql_conn = None
|
|
||||||
self.peer = peer
|
|
||||||
self.peers_semaphore = peers_semaphore
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.peers_semaphore.acquire()
|
|
||||||
get_peers(self.peer)
|
|
||||||
self.peers_semaphore.release()
|
|
||||||
|
|
||||||
# Returns the parameter from the specified file
|
|
||||||
def get_parameter( parameter, file_path ):
|
|
||||||
# Check if secrets file exists
|
|
||||||
if not os.path.isfile(file_path):
|
|
||||||
print("File %s not found, exiting."%file_path)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# Find parameter in file
|
|
||||||
with open( file_path ) as f:
|
|
||||||
for line in f:
|
|
||||||
if line.startswith( parameter ):
|
|
||||||
return line.replace(parameter + ":", "").strip()
|
|
||||||
|
|
||||||
# Cannot find parameter, exit
|
|
||||||
print(file_path + " Missing parameter %s "%parameter)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# main
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
|
||||||
start_time = time.time()
|
|
||||||
|
|
||||||
updated_at = datetime.now()
|
|
||||||
|
|
||||||
peers_api = '/api/v1/instance/peers?'
|
|
||||||
|
|
||||||
# Load configuration from config file
|
|
||||||
config_filepath = "config/config.txt"
|
|
||||||
mastodon_hostname = get_parameter("mastodon_hostname", config_filepath)
|
|
||||||
|
|
||||||
# Load database config from db_config file
|
|
||||||
db_config_filepath = "config/db_config.txt"
|
|
||||||
fediverse_db = get_parameter("fediverse_db", db_config_filepath)
|
|
||||||
fediverse_db_user = get_parameter("fediverse_db_user", db_config_filepath)
|
|
||||||
|
|
||||||
world_peers = []
|
|
||||||
|
|
||||||
user_agent = {'User-agent': 'Mozilla/5.0'}
|
|
||||||
|
|
||||||
res = requests.get('https://' + mastodon_hostname + peers_api, headers = user_agent, timeout=3)
|
|
||||||
|
|
||||||
hostname_peers = res.json()
|
|
||||||
|
|
||||||
for peer in hostname_peers:
|
|
||||||
|
|
||||||
exist_count = world_peers.count(peer)
|
|
||||||
|
|
||||||
if exist_count == 0:
|
|
||||||
|
|
||||||
world_peers.append(peer)
|
|
||||||
|
|
||||||
peers_semaphore = threading.Semaphore(100)
|
|
||||||
peer_threads = []
|
|
||||||
for peer in hostname_peers:
|
|
||||||
p_thread = Peerthreads(peer, peers_semaphore)
|
|
||||||
peer_threads.append(p_thread)
|
|
||||||
p_thread.start()
|
|
||||||
for p_thread in peer_threads:
|
|
||||||
p_thread.join()
|
|
||||||
|
|
||||||
write_servers(world_peers)
|
|
||||||
|
|
||||||
exec_time = str(round((time.time() - start_time), 2))
|
|
||||||
print(exec_time)
|
|
300
query.py
Normal file
300
query.py
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import os.path
|
||||||
|
import requests
|
||||||
|
import urllib3
|
||||||
|
import socket
|
||||||
|
from database import Database
|
||||||
|
from setup import Setup
|
||||||
|
import pdb
|
||||||
|
|
||||||
|
apis = ['/api/v1/instance?',
|
||||||
|
'/api/v1/nodeinfo?',
|
||||||
|
'/nodeinfo/2.0?',
|
||||||
|
'/nodeinfo/2.0.json?',
|
||||||
|
'/nodeinfo/2.1.json?',
|
||||||
|
'/main/nodeinfo/2.0?',
|
||||||
|
'/api/statusnet/config?',
|
||||||
|
'/api/nodeinfo/2.0.json?',
|
||||||
|
'/api/nodeinfo?',
|
||||||
|
'/wp-json/nodeinfo/2.0?',
|
||||||
|
'/api/v1/instance/nodeinfo/2.0?',
|
||||||
|
'/.well-known/x-nodeinfo2?'
|
||||||
|
]
|
||||||
|
|
||||||
|
def is_json(myjson):
|
||||||
|
|
||||||
|
try:
|
||||||
|
json_object = json.loads(myjson)
|
||||||
|
except ValueError as e:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
class Query():
|
||||||
|
|
||||||
|
name = "Query server data"
|
||||||
|
|
||||||
|
def __init__(self, server=None, db=None, setup=None, soft=None, soft_version=None, version=None, users=None, mau=None, alive=False):
|
||||||
|
|
||||||
|
self.server = server
|
||||||
|
self.db = Database()
|
||||||
|
self.setup = Setup()
|
||||||
|
self.soft = ''
|
||||||
|
self.soft_version = ''
|
||||||
|
self.users = 0
|
||||||
|
self.mau = 0
|
||||||
|
self.alive = alive
|
||||||
|
|
||||||
|
def getsoft(self, server):
|
||||||
|
|
||||||
|
if server.find(".") == -1:
|
||||||
|
return
|
||||||
|
if server.find("@") != -1:
|
||||||
|
return
|
||||||
|
if server.find("/") != -1:
|
||||||
|
return
|
||||||
|
if server.find(":") != -1:
|
||||||
|
return
|
||||||
|
|
||||||
|
is_nodeinfo = False
|
||||||
|
|
||||||
|
url = 'https://' + server
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
response = requests.get(url + '/.well-known/nodeinfo', headers = self.setup.user_agent, timeout=3)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
response_json = response.json()
|
||||||
|
|
||||||
|
if len(response_json['links']) == 1:
|
||||||
|
|
||||||
|
nodeinfo = response_json['links'][0]['href'].replace(f'https://{server}','')
|
||||||
|
|
||||||
|
elif len(response_json['links']) == 2:
|
||||||
|
|
||||||
|
nodeinfo = response_json['links'][1]['href'].replace(f'https://{server}','')
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
nodeinfo_data = requests.get(url + nodeinfo, headers = self.setup.user_agent, timeout=3)
|
||||||
|
|
||||||
|
if nodeinfo_data.status_code == 200:
|
||||||
|
|
||||||
|
nodeinfo_json = nodeinfo_data.json()
|
||||||
|
|
||||||
|
is_nodeinfo = True
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
print(f"{nodeinfo} not responding: error code {nodeinfo_data.status_code}")
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
print(f'{server} is not responding: error code {response.status_code}')
|
||||||
|
print('*********************************************************************')
|
||||||
|
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
|
||||||
|
for api in apis:
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
response = requests.get(url + api, headers = self.setup.user_agent, timeout=3)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
|
||||||
|
if is_json(response.text):
|
||||||
|
|
||||||
|
nodeinfo_json = response.json()
|
||||||
|
|
||||||
|
if 'software' in nodeinfo_json:
|
||||||
|
|
||||||
|
nodeinfo = api
|
||||||
|
|
||||||
|
is_nodeinfo = True
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
elif 'title' in nodeinfo_json:
|
||||||
|
|
||||||
|
if nodeinfo_json['title'] == 'Zap':
|
||||||
|
|
||||||
|
nodeinfo = api
|
||||||
|
|
||||||
|
is_nodeinfo = True
|
||||||
|
|
||||||
|
soft = 'zap'
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
elif 'version' in nodeinfo_json:
|
||||||
|
|
||||||
|
nodeinfo = api
|
||||||
|
|
||||||
|
is_nodeinfo = True
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
except requests.exceptions.SSLError as errssl:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
except requests.exceptions.HTTPError as errh:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
except requests.exceptions.ConnectionError as errc:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
except requests.exceptions.ReadTimeout as to_err:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
except requests.exceptions.TooManyRedirects as tmr_err:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
except urllib3.exceptions.LocationParseError as lp_err:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
except requests.exceptions.InvalidURL as iu_err:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
except requests.exceptions.ChunkedEncodingError as chunk_err:
|
||||||
|
|
||||||
|
print(f'ChunkedEncodingError! {server}')
|
||||||
|
pass
|
||||||
|
|
||||||
|
except ray.exceptions.RaySystemError as ray_sys_error:
|
||||||
|
|
||||||
|
print(ray_sys_error)
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
if is_nodeinfo:
|
||||||
|
|
||||||
|
if nodeinfo != '/api/v1/instance?':
|
||||||
|
|
||||||
|
if nodeinfo != '/.well-known/x-nodeinfo2?':
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
self.soft = nodeinfo_json['software']['name']
|
||||||
|
self.soft = self.soft.lower()
|
||||||
|
self.soft_version = nodeinfo_json['software']['version']
|
||||||
|
self.users = nodeinfo_json.get('usage').get('users').get('total') or '0'
|
||||||
|
if int(self.users) > 1000000:
|
||||||
|
return
|
||||||
|
self.mau = nodeinfo_json.get('usage').get('users').get('activeMonth') or 0
|
||||||
|
|
||||||
|
self.alive = True
|
||||||
|
|
||||||
|
self.db.write_api(server, self.soft, self.users, self.mau, self.alive, nodeinfo, self.soft_version)
|
||||||
|
|
||||||
|
print(f"Server {server} ({self.soft} {self.soft_version}) is alive!")
|
||||||
|
print('*********************************************************************')
|
||||||
|
|
||||||
|
return (server, self.soft, self.soft_version, self.users, self.mau, self.alive)
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
self.soft = nodeinfo_json['server']['software']
|
||||||
|
self.soft = soft.lower()
|
||||||
|
self.soft_version = nodeinfo_json['server']['version']
|
||||||
|
self.users = nodeinfo_json['usage']['users']['total']
|
||||||
|
if int(self.users) > 1000000:
|
||||||
|
return
|
||||||
|
self.mau = nodeinfo_json.get('usage').get('users').get('activeMonth') or 0
|
||||||
|
|
||||||
|
self.alive = True
|
||||||
|
|
||||||
|
if self.soft == 'socialhome':
|
||||||
|
|
||||||
|
self.db.write_api(server, soft, users, alive, nodeinfo, soft_version)
|
||||||
|
|
||||||
|
print('*********************************************************************')
|
||||||
|
print(f"Server {server} ({self.soft} {self.soft_version}) is alive!")
|
||||||
|
print('*********************************************************************')
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
if self.soft == '' and nodeinfo == "/api/v1/instance?":
|
||||||
|
|
||||||
|
self.soft = 'mastodon'
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
self.users = nodeinfo_json['stats']['user_count']
|
||||||
|
|
||||||
|
if int(self.users) > 1000000:
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
self.users = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
self.soft_version = nodeinfo_json['version']
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
self.soft_version = 'unknown'
|
||||||
|
|
||||||
|
alive = True
|
||||||
|
|
||||||
|
self.db.write_api(server, self.soft, self.users, self.alive, nodeinfo, self.soft_version)
|
||||||
|
|
||||||
|
print('*********************************************************************')
|
||||||
|
print(f"Server {server} ({self.soft}) is alive!")
|
||||||
|
|
||||||
|
elif self.soft == 'zap' and nodeinfo == "/api/v1/instance?":
|
||||||
|
|
||||||
|
self.soft = 'zap'
|
||||||
|
self.users = nodeinfo_json['stats']['user_count']
|
||||||
|
self.soft_version = nodeinfo_json['version']
|
||||||
|
self.alive = True
|
||||||
|
|
||||||
|
print(server, self.soft, self.users, self.alive, api)
|
||||||
|
|
||||||
|
print('*********************************************************************')
|
||||||
|
print(f"Server {server} ({self.soft}) is alive!")
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
print(f'Server {server} is dead')
|
||||||
|
print('*********************************************************************')
|
||||||
|
|
||||||
|
return (server, self.soft, self.soft_version, self.users, self.mau, self.alive)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
Mastodon.py>=1.5.1
|
requests
|
||||||
psycopg2-binary>=2.8.4
|
psycopg2-binary
|
||||||
aiohttp>=3.6.2
|
pytz
|
||||||
aiodns>=2.0.0
|
ray
|
||||||
matplotlib>=3.3.4
|
Mastodon.py
|
||||||
humanfriendly>=9.2
|
matplotlib
|
||||||
|
pandas
|
||||||
|
humanfriendly
|
||||||
|
|
273
setup.py
273
setup.py
|
@ -1,226 +1,157 @@
|
||||||
import getpass
|
|
||||||
from mastodon import Mastodon
|
|
||||||
from mastodon.Mastodon import MastodonMalformedEventError, MastodonNetworkError, MastodonReadTimeout, MastodonAPIError, MastodonIllegalArgumentError
|
|
||||||
import fileinput,re
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
import pytz
|
||||||
|
from mastodon import Mastodon
|
||||||
|
from mastodon.Mastodon import MastodonMalformedEventError, MastodonNetworkError, MastodonReadTimeout, MastodonAPIError, MastodonIllegalArgumentError
|
||||||
|
import pdb
|
||||||
|
|
||||||
def create_dir():
|
class Setup():
|
||||||
|
|
||||||
if not os.path.exists('secrets'):
|
name = 'fediverse setup'
|
||||||
os.makedirs('secrets')
|
|
||||||
|
|
||||||
def create_file():
|
def __init__(self, config_file=None, mastodon_hostname=None, peers_api=None, user_agent=None, secrets_filepath=None, mastodon_app_token=None):
|
||||||
|
|
||||||
if not os.path.exists('secrets/secrets.txt'):
|
self.config_file = "config/config.txt"
|
||||||
with open('secrets/secrets.txt', 'w'): pass
|
self.mastodon_hostname = self.__get_parameter("mastodon_hostname", self.config_file)
|
||||||
print(secrets_filepath + " created!")
|
self.peers_api = '/api/v1/instance/peers?'
|
||||||
|
self.user_agent = {'User-agent': "fediverse's stats (fediverse@mastodont.cat)"}
|
||||||
|
|
||||||
def create_config():
|
self.secrets_filepath = 'secrets/secrets.txt'
|
||||||
|
|
||||||
if not os.path.exists('config'):
|
is_setup = self.__check_mastodon_setup(self)
|
||||||
os.makedirs('config')
|
|
||||||
if not os.path.exists(config_filepath):
|
|
||||||
print(config_filepath + " created!")
|
|
||||||
with open('config/config.txt', 'w'): pass
|
|
||||||
|
|
||||||
def write_params():
|
if is_setup:
|
||||||
|
|
||||||
with open(secrets_filepath, 'a') as the_file:
|
self.mastodon_app_token = self.__get_mastodon_parameter("mastodon_app_token", self.secrets_filepath)
|
||||||
print("Writing secrets parameter names to " + secrets_filepath)
|
|
||||||
the_file.write('uc_client_id: \n'+'uc_client_secret: \n'+'uc_access_token: \n')
|
|
||||||
|
|
||||||
def write_config():
|
else:
|
||||||
|
|
||||||
with open(config_filepath, 'a') as the_file:
|
self.mastodon_app_token = self.mastodon_setup(self)
|
||||||
the_file.write('mastodon_hostname: \n')
|
|
||||||
print("adding parameter name 'mastodon_hostname' to "+ config_filepath)
|
|
||||||
|
|
||||||
def read_client_lines(self):
|
@staticmethod
|
||||||
|
def __check_mastodon_setup(self):
|
||||||
|
|
||||||
client_path = 'app_clientcred.txt'
|
is_setup = False
|
||||||
|
|
||||||
with open(client_path) as fp:
|
if not os.path.isfile(self.secrets_filepath):
|
||||||
|
print(f"File {self.secrets_filepath} not found, running setup.")
|
||||||
|
else:
|
||||||
|
is_setup = True
|
||||||
|
|
||||||
line = fp.readline()
|
return is_setup
|
||||||
cnt = 1
|
|
||||||
while line:
|
|
||||||
if cnt == 1:
|
|
||||||
print("Writing client id to " + secrets_filepath)
|
|
||||||
modify_file(secrets_filepath, "uc_client_id: ", value=line.rstrip())
|
|
||||||
elif cnt == 2:
|
|
||||||
print("Writing client secret to " + secrets_filepath)
|
|
||||||
modify_file(secrets_filepath, "uc_client_secret: ", value=line.rstrip())
|
|
||||||
line = fp.readline()
|
|
||||||
cnt += 1
|
|
||||||
|
|
||||||
def read_token_line(self):
|
@staticmethod
|
||||||
|
def mastodon_setup(self):
|
||||||
|
|
||||||
token_path = 'app_usercred.txt'
|
if not os.path.exists('secrets'):
|
||||||
|
os.makedirs('secrets')
|
||||||
|
|
||||||
with open(token_path) as fp:
|
self.mastodon_user = input("Mastodon user login? ")
|
||||||
|
self.mastodon_password = input("Mastodon user password? ")
|
||||||
|
self.app_name = 'fediverse'
|
||||||
|
|
||||||
line = fp.readline()
|
self.mastodon_app_token = self.mastodon_log_in()
|
||||||
print("Writing access token to " + secrets_filepath)
|
|
||||||
modify_file(secrets_filepath, "uc_access_token: ", value=line.rstrip())
|
|
||||||
|
|
||||||
def read_config_line():
|
if not os.path.exists(self.secrets_filepath):
|
||||||
|
with open(self.secrets_filepath, 'w'): pass
|
||||||
|
print(f"{self.secrets_filepath} created!")
|
||||||
|
|
||||||
with open(config_filepath) as fp:
|
with open(self.secrets_filepath, 'a') as the_file:
|
||||||
|
print("Writing Mastodon parameters to " + self.secrets_filepath)
|
||||||
|
the_file.write(f'mastodon_app_token: {self.mastodon_app_token}')
|
||||||
|
|
||||||
line = fp.readline()
|
return self.mastodon_app_token
|
||||||
modify_file(config_filepath, "mastodon_hostname: ", value=hostname)
|
|
||||||
modify_file(config_filepath, "bot_username: ", value=bot_username)
|
|
||||||
|
|
||||||
def log_in():
|
def mastodon_log_in(self):
|
||||||
|
|
||||||
error = 0
|
token = ''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
global hostname
|
response = Mastodon.create_app(
|
||||||
hostname = input("Enter Mastodon hostname: ")
|
self.app_name,
|
||||||
user_name = input("User name, ex. user@" + hostname +"? ")
|
scopes=["read","write"],
|
||||||
user_password = getpass.getpass("User password? ")
|
to_file=None,
|
||||||
bot_username = input("Bot's username, ex. fediverse: ")
|
api_base_url=self.mastodon_hostname
|
||||||
app_name = input("This app name? ")
|
)
|
||||||
Mastodon.create_app(
|
client_id = response[0]
|
||||||
app_name,
|
client_secret = response[1]
|
||||||
scopes=["read","write"],
|
|
||||||
to_file="app_clientcred.txt",
|
mastodon = Mastodon(client_id = client_id, client_secret = client_secret, api_base_url = self.mastodon_hostname)
|
||||||
api_base_url=hostname
|
|
||||||
)
|
token = mastodon.log_in(
|
||||||
mastodon = Mastodon(client_id = "app_clientcred.txt", api_base_url = hostname)
|
self.mastodon_user,
|
||||||
mastodon.log_in(
|
self.mastodon_password,
|
||||||
user_name,
|
scopes = ["read", "write"],
|
||||||
user_password,
|
to_file = None
|
||||||
scopes = ["read", "write"],
|
|
||||||
to_file = "app_usercred.txt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
except MastodonIllegalArgumentError as i_error:
|
print('Log in succesful!')
|
||||||
|
|
||||||
error = 1
|
except MastodonIllegalArgumentError as i_error:
|
||||||
|
|
||||||
if os.path.exists("secrets/secrets.txt"):
|
sys.stdout.write(f'\n{str(i_error)}\n')
|
||||||
print("Removing secrets/secrets.txt file..")
|
|
||||||
os.remove("secrets/secrets.txt")
|
|
||||||
if os.path.exists("app_clientcred.txt"):
|
|
||||||
print("Removing app_clientcred.txt file..")
|
|
||||||
os.remove("app_clientcred.txt")
|
|
||||||
sys.exit(i_error)
|
|
||||||
|
|
||||||
except MastodonNetworkError as n_error:
|
except MastodonNetworkError as n_error:
|
||||||
|
|
||||||
error = 1
|
sys.stdout.write(f'\n{str(n_error)}\n')
|
||||||
|
|
||||||
if os.path.exists("secrets/secrets.txt"):
|
except MastodonReadTimeout as r_error:
|
||||||
print("Removing secrets/secrets.txt file..")
|
|
||||||
os.remove("secrets/secrets.txt")
|
|
||||||
if os.path.exists("app_clientcred.txt"):
|
|
||||||
print("Removing app_clientcred.txt file..")
|
|
||||||
os.remove("app_clientcred.txt")
|
|
||||||
sys.exit(n_error)
|
|
||||||
|
|
||||||
except MastodonReadTimeout as r_error:
|
sys.stdout.write(f'\n{str(r_error)}\n')
|
||||||
|
|
||||||
error = 1
|
except MastodonAPIError as a_error:
|
||||||
|
|
||||||
if os.path.exists("secrets/secrets.txt"):
|
sys.stdout.write(f'\n{str(a_error)}\n')
|
||||||
print("Removing secrets/secrets.txt file..")
|
|
||||||
os.remove("secrets/secrets.txt")
|
|
||||||
if os.path.exists("app_clientcred.txt"):
|
|
||||||
print("Removing app_clientcred.txt file..")
|
|
||||||
os.remove("app_clientcred.txt")
|
|
||||||
sys.exit(r_error)
|
|
||||||
|
|
||||||
except MastodonAPIError as a_error:
|
finally:
|
||||||
|
|
||||||
error = 1
|
return token
|
||||||
|
|
||||||
if os.path.exists("secrets/secrets.txt"):
|
def __get_parameter(self, parameter, config_file):
|
||||||
print("Removing secrets/secrets.txt file..")
|
|
||||||
os.remove("secrets/secrets.txt")
|
|
||||||
if os.path.exists("app_clientcred.txt"):
|
|
||||||
print("Removing app_clientcred.txt file..")
|
|
||||||
os.remove("app_clientcred.txt")
|
|
||||||
sys.exit(a_error)
|
|
||||||
|
|
||||||
finally:
|
if not os.path.isfile(config_file):
|
||||||
|
print(f"File {config_file} not found..")
|
||||||
|
|
||||||
if error == 0:
|
self.mastodon_hostname = input("\nMastodon hostname: ")
|
||||||
|
|
||||||
create_dir()
|
self.__create_config(self)
|
||||||
create_file()
|
self.__write_config(self)
|
||||||
write_params()
|
|
||||||
client_path = 'app_clientcred.txt'
|
|
||||||
read_client_lines(client_path)
|
|
||||||
token_path = 'app_usercred.txt'
|
|
||||||
read_token_line(token_path)
|
|
||||||
if os.path.exists("app_clientcred.txt"):
|
|
||||||
print("Removing app_clientcred.txt temp file..")
|
|
||||||
os.remove("app_clientcred.txt")
|
|
||||||
if os.path.exists("app_usercred.txt"):
|
|
||||||
print("Removing app_usercred.txt temp file..")
|
|
||||||
os.remove("app_usercred.txt")
|
|
||||||
print("Secrets setup done!\n")
|
|
||||||
|
|
||||||
def modify_file(file_name,pattern,value=""):
|
with open( self.config_file ) as f:
|
||||||
|
for line in f:
|
||||||
|
if line.startswith( parameter ):
|
||||||
|
return line.replace(parameter + ":", "").strip()
|
||||||
|
|
||||||
fh=fileinput.input(file_name,inplace=True)
|
def __get_mastodon_parameter(self, parameter, secrets_filepath):
|
||||||
for line in fh:
|
|
||||||
replacement=pattern + value
|
|
||||||
line=re.sub(pattern,replacement,line)
|
|
||||||
sys.stdout.write(line)
|
|
||||||
fh.close()
|
|
||||||
|
|
||||||
def get_parameter( parameter, file_path ):
|
if not os.path.isfile(secrets_filepath):
|
||||||
# Check if secrets file exists
|
print(f"File {secrets_filepath} not found..")
|
||||||
if not os.path.isfile(file_path):
|
|
||||||
print("File %s not found, creating it."%file_path)
|
|
||||||
log_in()
|
|
||||||
|
|
||||||
# Find parameter in file
|
self.sign_in()
|
||||||
with open( file_path ) as f:
|
|
||||||
for line in f:
|
|
||||||
if line.startswith( parameter ):
|
|
||||||
return line.replace(parameter + ":", "").strip()
|
|
||||||
|
|
||||||
# Cannot find parameter, exit
|
with open( self.secrets_filepath ) as f:
|
||||||
print(file_path + " Missing parameter %s "%parameter)
|
for line in f:
|
||||||
sys.exit(0)
|
if line.startswith( parameter ):
|
||||||
|
return line.replace(parameter + ":", "").strip()
|
||||||
|
|
||||||
def get_hostname( parameter, config_filepath ):
|
@staticmethod
|
||||||
# Check if secrets file exists
|
def __create_config(self):
|
||||||
if not os.path.isfile(config_filepath):
|
|
||||||
print("File %s not found, creating it."%config_filepath)
|
|
||||||
create_config()
|
|
||||||
|
|
||||||
# Find parameter in file
|
if not os.path.exists('config'):
|
||||||
with open( config_filepath ) as f:
|
|
||||||
for line in f:
|
|
||||||
if line.startswith( parameter ):
|
|
||||||
return line.replace(parameter + ":", "").strip()
|
|
||||||
|
|
||||||
# Cannot find parameter, exit
|
os.makedirs('config')
|
||||||
print(config_filepath + " Missing parameter %s "%parameter)
|
|
||||||
write_config()
|
|
||||||
read_config_line()
|
|
||||||
print("setup done!")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
###############################################################################
|
if not os.path.exists(self.config_file):
|
||||||
# main
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
print(self.config_file + " created!")
|
||||||
|
with open(self.config_file, 'w'): pass
|
||||||
|
|
||||||
# Load secrets from secrets file
|
@staticmethod
|
||||||
secrets_filepath = "secrets/secrets.txt"
|
def __write_config(self):
|
||||||
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
|
with open(self.config_file, 'a') as the_file:
|
||||||
config_filepath = "config/config.txt"
|
|
||||||
mastodon_hostname = get_hostname("mastodon_hostname", config_filepath)
|
the_file.write(f'mastodon_hostname: {self.mastodon_hostname}')
|
||||||
bot_username = get_parameter("bot_username", config_filepath)
|
print(f"adding parameters to {self.config_file}\n")
|
||||||
|
|
165
uptime.py
165
uptime.py
|
@ -2,165 +2,24 @@ import time
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import humanfriendly
|
import humanfriendly
|
||||||
|
from setup import Setup
|
||||||
|
from database import Database
|
||||||
from mastodon import Mastodon
|
from mastodon import Mastodon
|
||||||
import psycopg2
|
|
||||||
|
|
||||||
def get_uptime():
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
conn = None
|
|
||||||
|
|
||||||
conn = psycopg2.connect(database=fediverse_db, user=fediverse_db_user, password="", host="/var/run/postgresql", port="5432")
|
|
||||||
|
|
||||||
cur = conn.cursor()
|
|
||||||
|
|
||||||
cur.execute("select count(server) from fediverse where alive")
|
|
||||||
|
|
||||||
row = cur.fetchone()
|
|
||||||
|
|
||||||
alive_servers = row[0]
|
|
||||||
|
|
||||||
cur.execute("select MAX(last_checked_at - first_checked_at) from fediverse")
|
|
||||||
|
|
||||||
row = cur.fetchone()
|
|
||||||
|
|
||||||
if row[0] != timedelta(0):
|
|
||||||
|
|
||||||
max_uptime = row[0]
|
|
||||||
|
|
||||||
cur.execute("select count(server) from fediverse where last_checked_at-first_checked_at=(%s)", (max_uptime,))
|
|
||||||
|
|
||||||
row = cur.fetchone()
|
|
||||||
|
|
||||||
if row is not None:
|
|
||||||
|
|
||||||
best_servers = row[0]
|
|
||||||
|
|
||||||
cur.execute("select software, count(*) as servers from fediverse where last_checked_at-first_checked_at=(%s) group by software order by servers desc", (max_uptime,))
|
|
||||||
|
|
||||||
rows = cur.fetchall()
|
|
||||||
|
|
||||||
software_lst = []
|
|
||||||
|
|
||||||
servers_lst = []
|
|
||||||
|
|
||||||
for row in rows:
|
|
||||||
|
|
||||||
software_lst.append(row[0])
|
|
||||||
|
|
||||||
servers_lst.append(row[1])
|
|
||||||
|
|
||||||
cur.close()
|
|
||||||
|
|
||||||
return (alive_servers, max_uptime, best_servers, software_lst, servers_lst)
|
|
||||||
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
|
|
||||||
if conn is not None:
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
def get_percentage(uptime_servers, software):
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
conn = None
|
|
||||||
|
|
||||||
conn = psycopg2.connect(database=fediverse_db, user=fediverse_db_user, password="", host="/var/run/postgresql", port="5432")
|
|
||||||
|
|
||||||
cur = conn.cursor()
|
|
||||||
|
|
||||||
cur.execute("select count(server) from fediverse where alive and software=(%s)", (software,))
|
|
||||||
|
|
||||||
row = cur.fetchone()
|
|
||||||
|
|
||||||
if row is not None:
|
|
||||||
|
|
||||||
soft_total_servers = row[0]
|
|
||||||
|
|
||||||
cur.close()
|
|
||||||
|
|
||||||
soft_uptime_percent = round((uptime_servers * 100) / soft_total_servers, 1)
|
|
||||||
|
|
||||||
return (soft_uptime_percent)
|
|
||||||
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
|
|
||||||
if conn is not None:
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
def mastodon():
|
|
||||||
|
|
||||||
# Load secrets from secrets file
|
|
||||||
secrets_filepath = "secrets/uptime_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/config.txt"
|
|
||||||
mastodon_hostname = get_parameter("mastodon_hostname", config_filepath)
|
|
||||||
|
|
||||||
# Initialise Mastodon API
|
|
||||||
mastodon = Mastodon(
|
|
||||||
client_id=uc_client_id,
|
|
||||||
client_secret=uc_client_secret,
|
|
||||||
access_token=uc_access_token,
|
|
||||||
api_base_url='https://' + mastodon_hostname,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Initialise access headers
|
|
||||||
headers = {'Authorization': 'Bearer %s'%uc_access_token}
|
|
||||||
|
|
||||||
return (mastodon, mastodon_hostname)
|
|
||||||
|
|
||||||
def db_config():
|
|
||||||
|
|
||||||
# Load db configuration from config file
|
|
||||||
config_filepath = "config/db_config.txt"
|
|
||||||
fediverse_db = get_parameter("fediverse_db", config_filepath)
|
|
||||||
fediverse_db_user = get_parameter("fediverse_db_user", config_filepath)
|
|
||||||
|
|
||||||
return (fediverse_db, fediverse_db_user)
|
|
||||||
|
|
||||||
# Returns the parameter from the specified file
|
|
||||||
def get_parameter(parameter, file_path):
|
|
||||||
|
|
||||||
# Check if secrets file exists
|
|
||||||
if not os.path.isfile(file_path):
|
|
||||||
print("File %s not found, exiting."%file_path)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# Find parameter in file
|
|
||||||
with open(file_path) as f:
|
|
||||||
for line in f:
|
|
||||||
if line.startswith(parameter):
|
|
||||||
return line.replace(parameter + ":", "").strip()
|
|
||||||
|
|
||||||
# Cannot find parameter, exit
|
|
||||||
print(file_path + " Missing parameter %s "%parameter)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# main
|
# main
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
mastodon, mastodon_hostname = mastodon()
|
setup = Setup()
|
||||||
|
|
||||||
fediverse_db, fediverse_db_user = db_config()
|
mastodon = Mastodon(
|
||||||
|
access_token = setup.mastodon_app_token,
|
||||||
|
api_base_url= setup.mastodon_hostname
|
||||||
|
)
|
||||||
|
|
||||||
alive_servers, max_uptime, best_servers, software_lst, servers_lst = get_uptime()
|
db = Database()
|
||||||
|
|
||||||
|
alive_servers, max_uptime, best_servers, software_lst, servers_lst = db.get_uptime()
|
||||||
|
|
||||||
toot_text = '\nAlive servers: ' + str(alive_servers)
|
toot_text = '\nAlive servers: ' + str(alive_servers)
|
||||||
toot_text += '\n\n'
|
toot_text += '\n\n'
|
||||||
|
@ -172,10 +31,10 @@ if __name__ == '__main__':
|
||||||
i = 0
|
i = 0
|
||||||
while i < len(software_lst):
|
while i < len(software_lst):
|
||||||
|
|
||||||
soft_percent = get_percentage(servers_lst[i], software_lst[i])
|
soft_percent = db.get_percentage(servers_lst[i], software_lst[i])
|
||||||
toot_text += ':' + str(software_lst[i]) + ': ' + str(servers_lst[i]) + ' (' + str(soft_percent) + '%)\n'
|
toot_text += ':' + str(software_lst[i]) + ': ' + str(servers_lst[i]) + ' (' + str(soft_percent) + '%)\n'
|
||||||
|
|
||||||
if len(toot_text) > 480:
|
if len(toot_text) > 470:
|
||||||
break
|
break
|
||||||
i +=1
|
i +=1
|
||||||
|
|
||||||
|
|
188
uptime_setup.py
188
uptime_setup.py
|
@ -1,188 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import getpass
|
|
||||||
from mastodon import Mastodon
|
|
||||||
from mastodon.Mastodon import MastodonMalformedEventError, MastodonNetworkError, MastodonReadTimeout, MastodonAPIError, MastodonIllegalArgumentError
|
|
||||||
import fileinput,re
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
def create_dir():
|
|
||||||
if not os.path.exists('secrets'):
|
|
||||||
os.makedirs('secrets')
|
|
||||||
|
|
||||||
def create_file():
|
|
||||||
if not os.path.exists('secrets/uptime_secrets.txt'):
|
|
||||||
with open('secrets/uptime_secrets.txt', 'w'): pass
|
|
||||||
print(secrets_filepath + " created!")
|
|
||||||
|
|
||||||
def create_config():
|
|
||||||
if not os.path.exists('config'):
|
|
||||||
os.makedirs('config')
|
|
||||||
if not os.path.exists(config_filepath):
|
|
||||||
print(config_filepath + " created!")
|
|
||||||
with open('config/config.txt', 'w'): pass
|
|
||||||
|
|
||||||
def write_params():
|
|
||||||
with open(secrets_filepath, 'a') as the_file:
|
|
||||||
print("Writing secrets parameter names to " + secrets_filepath)
|
|
||||||
the_file.write('uc_client_id: \n'+'uc_client_secret: \n'+'uc_access_token: \n')
|
|
||||||
|
|
||||||
def write_config():
|
|
||||||
with open(config_filepath, 'a') as the_file:
|
|
||||||
the_file.write('mastodon_hostname: \n')
|
|
||||||
print("adding parameter name 'mastodon_hostname' to "+ config_filepath)
|
|
||||||
|
|
||||||
def read_client_lines(self):
|
|
||||||
client_path = 'app_clientcred.txt'
|
|
||||||
with open(client_path) as fp:
|
|
||||||
line = fp.readline()
|
|
||||||
cnt = 1
|
|
||||||
while line:
|
|
||||||
if cnt == 1:
|
|
||||||
print("Writing client id to " + secrets_filepath)
|
|
||||||
modify_file(secrets_filepath, "uc_client_id: ", value=line.rstrip())
|
|
||||||
elif cnt == 2:
|
|
||||||
print("Writing client secret to " + secrets_filepath)
|
|
||||||
modify_file(secrets_filepath, "uc_client_secret: ", value=line.rstrip())
|
|
||||||
line = fp.readline()
|
|
||||||
cnt += 1
|
|
||||||
|
|
||||||
def read_token_line(self):
|
|
||||||
token_path = 'app_usercred.txt'
|
|
||||||
with open(token_path) as fp:
|
|
||||||
line = fp.readline()
|
|
||||||
print("Writing access token to " + secrets_filepath)
|
|
||||||
modify_file(secrets_filepath, "uc_access_token: ", value=line.rstrip())
|
|
||||||
|
|
||||||
def read_config_line():
|
|
||||||
with open(config_filepath) as fp:
|
|
||||||
line = fp.readline()
|
|
||||||
modify_file(config_filepath, "mastodon_hostname: ", value=hostname)
|
|
||||||
|
|
||||||
def log_in():
|
|
||||||
error = 0
|
|
||||||
try:
|
|
||||||
global hostname
|
|
||||||
hostname = input("Enter Mastodon hostname: ")
|
|
||||||
user_name = input("User name, ex. user@" + hostname +"? ")
|
|
||||||
user_password = getpass.getpass("User password? ")
|
|
||||||
app_name = input("This app name? ")
|
|
||||||
Mastodon.create_app(app_name, scopes=["read","write"],
|
|
||||||
to_file="app_clientcred.txt", api_base_url=hostname)
|
|
||||||
mastodon = Mastodon(client_id = "app_clientcred.txt", api_base_url = hostname)
|
|
||||||
mastodon.log_in(
|
|
||||||
user_name,
|
|
||||||
user_password,
|
|
||||||
scopes = ["read", "write"],
|
|
||||||
to_file = "app_usercred.txt"
|
|
||||||
)
|
|
||||||
except MastodonIllegalArgumentError as i_error:
|
|
||||||
error = 1
|
|
||||||
if os.path.exists("secrets/uptime_secrets.txt"):
|
|
||||||
print("Removing secrets/uptime_secrets.txt file..")
|
|
||||||
os.remove("secrets/uptime_secrets.txt")
|
|
||||||
if os.path.exists("app_clientcred.txt"):
|
|
||||||
print("Removing app_clientcred.txt file..")
|
|
||||||
os.remove("app_clientcred.txt")
|
|
||||||
sys.exit(i_error)
|
|
||||||
except MastodonNetworkError as n_error:
|
|
||||||
error = 1
|
|
||||||
if os.path.exists("secrets/uptime_secrets.txt"):
|
|
||||||
print("Removing secrets/uptime_secrets.txt file..")
|
|
||||||
os.remove("secrets/uptime_secrets.txt")
|
|
||||||
if os.path.exists("app_clientcred.txt"):
|
|
||||||
print("Removing app_clientcred.txt file..")
|
|
||||||
os.remove("app_clientcred.txt")
|
|
||||||
sys.exit(n_error)
|
|
||||||
except MastodonReadTimeout as r_error:
|
|
||||||
error = 1
|
|
||||||
if os.path.exists("secrets/uptime_secrets.txt"):
|
|
||||||
print("Removing secrets/uptime_secrets.txt file..")
|
|
||||||
os.remove("secrets/uptime_secrets.txt")
|
|
||||||
if os.path.exists("app_clientcred.txt"):
|
|
||||||
print("Removing app_clientcred.txt file..")
|
|
||||||
os.remove("app_clientcred.txt")
|
|
||||||
sys.exit(r_error)
|
|
||||||
except MastodonAPIError as a_error:
|
|
||||||
error = 1
|
|
||||||
if os.path.exists("secrets/uptime_secrets.txt"):
|
|
||||||
print("Removing secrets/uptime_secrets.txt file..")
|
|
||||||
os.remove("secrets/uptime_secrets.txt")
|
|
||||||
if os.path.exists("app_clientcred.txt"):
|
|
||||||
print("Removing app_clientcred.txt file..")
|
|
||||||
os.remove("app_clientcred.txt")
|
|
||||||
sys.exit(a_error)
|
|
||||||
finally:
|
|
||||||
if error == 0:
|
|
||||||
|
|
||||||
create_dir()
|
|
||||||
create_file()
|
|
||||||
write_params()
|
|
||||||
client_path = 'app_clientcred.txt'
|
|
||||||
read_client_lines(client_path)
|
|
||||||
token_path = 'app_usercred.txt'
|
|
||||||
read_token_line(token_path)
|
|
||||||
if os.path.exists("app_clientcred.txt"):
|
|
||||||
print("Removing app_clientcred.txt temp file..")
|
|
||||||
os.remove("app_clientcred.txt")
|
|
||||||
if os.path.exists("app_usercred.txt"):
|
|
||||||
print("Removing app_usercred.txt temp file..")
|
|
||||||
os.remove("app_usercred.txt")
|
|
||||||
print("uptime secrets setup done!\n")
|
|
||||||
|
|
||||||
def modify_file(file_name,pattern,value=""):
|
|
||||||
fh=fileinput.input(file_name,inplace=True)
|
|
||||||
for line in fh:
|
|
||||||
replacement=pattern + value
|
|
||||||
line=re.sub(pattern,replacement,line)
|
|
||||||
sys.stdout.write(line)
|
|
||||||
fh.close()
|
|
||||||
|
|
||||||
# Returns the parameter from the specified file
|
|
||||||
def get_parameter( parameter, file_path ):
|
|
||||||
# Check if secrets file exists
|
|
||||||
if not os.path.isfile(file_path):
|
|
||||||
print("File %s not found, creating it."%file_path)
|
|
||||||
log_in()
|
|
||||||
|
|
||||||
# Find parameter in file
|
|
||||||
with open( file_path ) as f:
|
|
||||||
for line in f:
|
|
||||||
if line.startswith( parameter ):
|
|
||||||
return line.replace(parameter + ":", "").strip()
|
|
||||||
|
|
||||||
# Cannot find parameter, exit
|
|
||||||
print(file_path + " Missing parameter %s "%parameter)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# Returns the parameter from the specified file
|
|
||||||
def get_hostname( parameter, config_filepath ):
|
|
||||||
# Check if secrets file exists
|
|
||||||
if not os.path.isfile(config_filepath):
|
|
||||||
print("File %s not found, creating it."%config_filepath)
|
|
||||||
create_config()
|
|
||||||
|
|
||||||
# Find parameter in file
|
|
||||||
with open( config_filepath ) as f:
|
|
||||||
for line in f:
|
|
||||||
if line.startswith( parameter ):
|
|
||||||
return line.replace(parameter + ":", "").strip()
|
|
||||||
|
|
||||||
# Cannot find parameter, exit
|
|
||||||
print(config_filepath + " Missing parameter %s "%parameter)
|
|
||||||
write_config()
|
|
||||||
read_config_line()
|
|
||||||
print("setup done!")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# Load secrets from secrets file
|
|
||||||
secrets_filepath = "secrets/uptime_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/config.txt"
|
|
||||||
#mastodon_hostname = get_hostname("mastodon_hostname", config_filepath)
|
|
Loading…
Referencia en una nova incidència