Plotting federated instances

merge-requests/30/head
Bob Mottram 2020-07-08 13:28:41 +01:00
parent b1516507f8
commit 7b06a3b262
4 changed files with 137 additions and 15 deletions

View File

@ -144,6 +144,15 @@ To list the domains referenced in public posts:
python3 epicyon.py --postDomains nickname@domain python3 epicyon.py --postDomains nickname@domain
``` ```
## Plotting federated instances
To plot a set of federated instances, based upon a sample of handles on those instances:
``` bash
python3 epicyon.py --socnet nickname1@domain1,nickname2@domain2,nickname3@domain3
xdot socnet.dot
```
## Delete posts ## Delete posts
To delete a post which you wrote you must first know its url. It is usually something like: To delete a post which you wrote you must first know its url. It is usually something like:

View File

@ -67,6 +67,7 @@ from shares import sendUndoShareViaServer
from shares import addShare from shares import addShare
from theme import setTheme from theme import setTheme
from announce import sendAnnounceViaServer from announce import sendAnnounceViaServer
from socnet import instancesGraph
import argparse import argparse
@ -151,6 +152,10 @@ parser.add_argument('--postDomains', dest='postDomains', type=str,
default=None, default=None,
help='Show domains referenced in public ' help='Show domains referenced in public '
'posts for the given handle') 'posts for the given handle')
parser.add_argument('--socnet', dest='socnet', type=str,
default=None,
help='Show dot diagram for social network '
'of federated instances')
parser.add_argument('--postsraw', dest='postsraw', type=str, parser.add_argument('--postsraw', dest='postsraw', type=str,
default=None, default=None,
help='Show raw json of posts for the given handle') help='Show raw json of posts for the given handle')
@ -449,13 +454,35 @@ if args.postDomains:
elif args.gnunet: elif args.gnunet:
proxyType = 'gnunet' proxyType = 'gnunet'
domainList = [] domainList = []
domainList = getPublicPostDomains(baseDir, nickname, domain, False, True, domainList = getPublicPostDomains(baseDir, nickname, domain,
proxyType, args.port, httpPrefix, debug, proxyType, args.port,
httpPrefix, debug,
__version__, domainList) __version__, domainList)
for postDomain in domainList: for postDomain in domainList:
print(postDomain) print(postDomain)
sys.exit() sys.exit()
if args.socnet:
if ',' not in args.socnet:
print('Syntax: '
'--socnet nick1@domain1,nick2@domain2,nick3@domain3')
sys.exit()
if not args.http:
args.port = 443
proxyType = 'tor'
dotGraph = instancesGraph(baseDir, args.socnet,
proxyType, args.port,
httpPrefix, debug,
__version__)
try:
with open('socnet.dot', 'w') as fp:
fp.write(dotGraph)
print('Saved to socnet.dot')
except BaseException:
pass
sys.exit()
if args.postsraw: if args.postsraw:
if '@' not in args.postsraw: if '@' not in args.postsraw:
print('Syntax: --postsraw nickname@domain') print('Syntax: --postsraw nickname@domain')

View File

@ -146,11 +146,14 @@ def getUserUrl(wfRequest: {}) -> str:
def parseUserFeed(session, feedUrl: str, asHeader: {}, def parseUserFeed(session, feedUrl: str, asHeader: {},
projectVersion: str, httpPrefix: str, projectVersion: str, httpPrefix: str,
domain: str) -> None: domain: str,depth=0) -> {}:
if depth > 10:
return None
feedJson = getJson(session, feedUrl, asHeader, None, feedJson = getJson(session, feedUrl, asHeader, None,
projectVersion, httpPrefix, domain) projectVersion, httpPrefix, domain)
if not feedJson: if not feedJson:
return return None
if 'orderedItems' in feedJson: if 'orderedItems' in feedJson:
for item in feedJson['orderedItems']: for item in feedJson['orderedItems']:
@ -168,9 +171,10 @@ def parseUserFeed(session, feedUrl: str, asHeader: {},
userFeed = \ userFeed = \
parseUserFeed(session, nextUrl, asHeader, parseUserFeed(session, nextUrl, asHeader,
projectVersion, httpPrefix, projectVersion, httpPrefix,
domain) domain, depth+1)
for item in userFeed: if userFeed:
yield item for item in userFeed:
yield item
elif isinstance(nextUrl, dict): elif isinstance(nextUrl, dict):
userFeed = nextUrl userFeed = nextUrl
if userFeed.get('orderedItems'): if userFeed.get('orderedItems'):
@ -444,8 +448,8 @@ def getPostDomains(session, outboxUrl: str, maxPosts: int,
maxMentions: int, maxMentions: int,
maxEmoji: int, maxAttachments: int, maxEmoji: int, maxAttachments: int,
federationList: [], federationList: [],
personCache: {}, raw: bool, personCache: {},
simple: bool, debug: bool, debug: bool,
projectVersion: str, httpPrefix: str, projectVersion: str, httpPrefix: str,
domain: str, domainList=[]) -> []: domain: str, domainList=[]) -> []:
"""Returns a list of domains referenced within public posts """Returns a list of domains referenced within public posts
@ -467,6 +471,9 @@ def getPostDomains(session, outboxUrl: str, maxPosts: int,
userFeed = parseUserFeed(session, outboxUrl, asHeader, userFeed = parseUserFeed(session, outboxUrl, asHeader,
projectVersion, httpPrefix, domain) projectVersion, httpPrefix, domain)
for item in userFeed: for item in userFeed:
i += 1
if i > maxPosts:
break
if not item.get('object'): if not item.get('object'):
continue continue
if not isinstance(item['object'], dict): if not isinstance(item['object'], dict):
@ -486,9 +493,6 @@ def getPostDomains(session, outboxUrl: str, maxPosts: int,
getDomainFromActor(tagItem['href']) getDomainFromActor(tagItem['href'])
if postDomain not in postDomains: if postDomain not in postDomains:
postDomains.append(postDomain) postDomains.append(postDomain)
i += 1
if i == maxPosts:
break
return postDomains return postDomains
@ -2986,8 +2990,7 @@ def getPublicPostsOfPerson(baseDir: str, nickname: str, domain: str,
def getPublicPostDomains(baseDir: str, nickname: str, domain: str, def getPublicPostDomains(baseDir: str, nickname: str, domain: str,
raw: bool, simple: bool, proxyType: str, proxyType: str, port: int, httpPrefix: str,
port: int, httpPrefix: str,
debug: bool, projectVersion: str, debug: bool, projectVersion: str,
domainList=[]) -> []: domainList=[]) -> []:
""" Returns a list of domains referenced within public posts """ Returns a list of domains referenced within public posts
@ -3028,7 +3031,7 @@ def getPublicPostDomains(baseDir: str, nickname: str, domain: str,
postDomains = \ postDomains = \
getPostDomains(session, personUrl, 64, maxMentions, maxEmoji, getPostDomains(session, personUrl, 64, maxMentions, maxEmoji,
maxAttachments, federationList, maxAttachments, federationList,
personCache, raw, simple, debug, personCache, debug,
projectVersion, httpPrefix, domain, domainList) projectVersion, httpPrefix, domain, domainList)
postDomains.sort() postDomains.sort()
return postDomains return postDomains

83
socnet.py 100644
View File

@ -0,0 +1,83 @@
__filename__ = "socnet.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.1.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
from session import createSession
from webfinger import webfingerHandle
from posts import getPersonBox
from posts import getPostDomains
def instancesGraph(baseDir: str, handles: str,
proxyType: str,
port: int, httpPrefix: str,
debug: bool, projectVersion: str) -> str:
""" Returns a dot graph of federating instances
based upon a few sample handles.
The handles argument should contain a comma separated list
of handles on different instances
"""
dotGraphStr = 'digraph instances {\n'
if ',' not in handles:
return dotGraphStr + '}\n'
session = createSession(proxyType)
if not session:
return dotGraphStr + '}\n'
personCache = {}
cachedWebfingers = {}
federationList = []
maxMentions = 99
maxEmoji = 99
maxAttachments = 5
personHandles = handles.split(',')
for handle in personHandles:
handle = handle.strip()
if handle.startswith('@'):
handle = handle[1:]
if '@' not in handle:
continue
nickname = handle.split('@')[0]
domain = handle.split('@')[1]
domainFull = domain
if port:
if port != 80 and port != 443:
if ':' not in domain:
domainFull = domain + ':' + str(port)
handle = httpPrefix + "://" + domainFull + "/@" + nickname
wfRequest = \
webfingerHandle(session, handle, httpPrefix,
cachedWebfingers,
domain, projectVersion)
if not wfRequest:
return dotGraphStr + '}\n'
if not isinstance(wfRequest, dict):
print('Webfinger for ' + handle + ' did not return a dict. ' +
str(wfRequest))
return dotGraphStr + '}\n'
(personUrl, pubKeyId, pubKey,
personId, shaedInbox,
capabilityAcquisition,
avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest,
personCache,
projectVersion, httpPrefix,
nickname, domain, 'outbox')
postDomains = \
getPostDomains(session, personUrl, 64, maxMentions, maxEmoji,
maxAttachments, federationList,
personCache, debug,
projectVersion, httpPrefix, domain, [])
postDomains.sort()
for fedDomain in postDomains:
dotLineStr = ' "' + domain + '" -> "' + fedDomain + '";\n'
if dotLineStr not in dotGraphStr:
dotGraphStr += dotLineStr
return dotGraphStr + '}\n'