Browse Source

Setting availability via c2s

Bob Mottram 4 months ago
parent
commit
81b0207c2a
5 changed files with 173 additions and 32 deletions
  1. 137 0
      availability.py
  2. 5 1
      daemon.py
  3. 27 10
      epicyon.py
  4. 0 17
      person.py
  5. 4 4
      skills.py

+ 137 - 0
availability.py

@@ -0,0 +1,137 @@
+__filename__ = "availability.py"
+__author__ = "Bob Mottram"
+__license__ = "AGPL3+"
+__version__ = "0.0.1"
+__maintainer__ = "Bob Mottram"
+__email__ = "bob@freedombone.net"
+__status__ = "Production"
+
+import json
+import commentjson
+import os
+from webfinger import webfingerHandle
+from auth import createBasicAuthHeader
+from posts import getPersonBox
+from session import postJson
+from utils import getNicknameFromActor
+from utils import getDomainFromActor
+
+def setAvailability(baseDir: str,nickname: str,domain: str, \
+                    status: str) -> bool:
+    """Set an availability status
+    """
+    # avoid giant strings
+    if len(status)>128:
+        return False
+    actorFilename=baseDir+'/accounts/'+nickname+'@'+domain+'.json'
+    if not os.path.isfile(actorFilename):
+        return False
+    with open(actorFilename, 'r') as fp:
+        actorJson=commentjson.load(fp)
+        actorJson['availability']=status
+        with open(actorFilename, 'w') as fp:
+            commentjson.dump(actorJson, fp, indent=4, sort_keys=False)    
+    return True
+
+def getAvailability(baseDir: str,nickname: str,domain: str) -> str:
+    """Returns the availability for a given person
+    """
+    actorFilename=baseDir+'/accounts/'+nickname+'@'+domain+'.json'
+    if not os.path.isfile(actorFilename):
+        return False
+    with open(actorFilename, 'r') as fp:
+        actorJson=commentjson.load(fp)
+        if not actorJson.get('availability'):
+            return None
+        return actorJson['availability']
+    return None
+
+def outboxAvailability(baseDir: str,nickname: str,messageJson: {},debug: bool) -> bool:
+    """Handles receiving an availability update
+    """
+    if not messageJson.get('type'):
+        return False
+    if not messageJson['type']=='Availability':
+        return False
+    if not messageJson.get('actor'):
+        return False
+    if not messageJson.get('object'):
+        return False
+    if not isinstance(messageJson['object'], str):
+        return False
+
+    actorNickname=getNicknameFromActor(messageJson['actor'])
+    if actorNickname!=nickname:
+        return False
+    domain,port=getDomainFromActor(messageJson['actor'])
+    status=messageJson['object'].replace('"','')
+
+    return setAvailability(baseDir,nickname,domain,status)
+
+def sendAvailabilityViaServer(session,nickname: str,password: str,
+                              domain: str,port: int, \
+                              httpPrefix: str, \
+                              status: str, \
+                              cachedWebfingers: {},personCache: {}, \
+                              debug: bool) -> {}:
+    """Sets the availability for a person via c2s
+    """
+    if not session:
+        print('WARN: No session for sendAvailabilityViaServer')
+        return 6
+
+    domainFull=domain
+    if port!=80 and port!=443:
+        domainFull=domain+':'+str(port)
+        
+    toUrl = httpPrefix+'://'+domainFull+'/users/'+nickname
+    ccUrl = httpPrefix+'://'+domainFull+'/users/'+nickname+'/followers'
+
+    newAvailabilityJson = {
+        'type': 'Availability',
+        'actor': httpPrefix+'://'+domainFull+'/users/'+nickname,
+        'object': '"'+status+'"',
+        'to': [toUrl],
+        'cc': [ccUrl]
+    }
+
+    handle=httpPrefix+'://'+domainFull+'/@'+nickname
+
+    # lookup the inbox for the To handle
+    wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers)
+    if not wfRequest:
+        if debug:
+            print('DEBUG: announce webfinger failed for '+handle)
+        return 1
+
+    postToBox='outbox'
+
+    # get the actor inbox for the To handle
+    inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition = \
+        getPersonBox(session,wfRequest,personCache,postToBox)
+                     
+    if not inboxUrl:
+        if debug:
+            print('DEBUG: No '+postToBox+' was found for '+handle)
+        return 3
+    if not fromPersonId:
+        if debug:
+            print('DEBUG: No actor was found for '+handle)
+        return 4
+    
+    authHeader=createBasicAuthHeader(Nickname,password)
+     
+    headers = {'host': domain, \
+               'Content-type': 'application/json', \
+               'Authorization': authHeader}
+    postResult = \
+        postJson(session,newAvailabilityJson,[],inboxUrl,headers,"inbox:write")
+    #if not postResult:
+    #    if debug:
+    #        print('DEBUG: POST announce failed for c2s to '+inboxUrl)
+    #    return 5
+
+    if debug:
+        print('DEBUG: c2s POST availability success')
+
+    return newAvailabilityJson

+ 5 - 1
daemon.py

@@ -43,6 +43,7 @@ from blocking import outboxUndoBlock
 from config import setConfigParam
 from roles import outboxDelegate
 from skills import outboxSkills
+from availability import outboxAvailability
 import os
 import sys
 
@@ -225,8 +226,11 @@ class PubServer(BaseHTTPRequestHandler):
             print('DEBUG: handle delegation requests')
         outboxDelegate(self.server.baseDir,self.postToNickname,messageJson,self.server.debug)
         if self.server.debug:
-            print('DEBUG: handle skills changes requestsw')
+            print('DEBUG: handle skills changes requests')
         outboxSkills(self.server.baseDir,self.postToNickname,messageJson,self.server.debug)
+        if self.server.debug:
+            print('DEBUG: handle availability changes requests')
+        outboxAvailability(self.server.baseDir,self.postToNickname,messageJson,self.server.debug)
         if self.server.debug:
             print('DEBUG: handle any like requests')
         outboxLike(self.server.baseDir,self.server.httpPrefix, \

+ 27 - 10
epicyon.py

@@ -6,7 +6,6 @@ __maintainer__ = "Bob Mottram"
 __email__ = "bob@freedombone.net"
 __status__ = "Production"
 
-
 from person import createPerson
 from person import createSharedInbox
 from person import createCapabilitiesInbox
@@ -16,7 +15,6 @@ from person import validNickname
 from person import setProfileImage
 from skills import setSkillLevel
 from roles import setRole
-from person import setAvailability
 from person import setOrganizationScheme
 from webfinger import webfingerHandle
 from posts import getPosts
@@ -69,6 +67,8 @@ from blocking import sendBlockViaServer
 from blocking import sendUndoBlockViaServer
 from roles import sendRoleViaServer
 from skills import sendSkillViaServer
+from availability import setAvailability
+from availability import sendAvailabilityViaServer
 import argparse
 
 def str2bool(v):
@@ -727,14 +727,6 @@ if args.backgroundImage:
         print('Background image was not added for '+args.nickname)
     sys.exit()    
 
-if args.availability:
-    if not nickname:
-        print('No nickname given')
-        sys.exit()
-    if setAvailability(baseDir,nickname,domain,args.availability):
-        print('Availablity set to '+args.availability)
-    sys.exit()
-
 if args.project:
     if not args.delegate and not args.undelegate:        
         if not nickname:
@@ -786,6 +778,31 @@ if args.skill:
         time.sleep(1)
     sys.exit()
 
+if args.availability:
+    if not nickname:
+        print('Specify a nickname with the --nickname option')
+        sys.exit()
+        
+    if not args.password:
+        print('Specify a password with the --password option')
+        sys.exit()
+
+    session = createSession(domain,port,useTor)        
+    personCache={}
+    cachedWebfingers={}
+    print('Sending availability status of '+nickname+' as '+args.availability)
+
+    sendAvailabilityViaServer(session,nickname,args.password,
+                              domain,port, \
+                              httpPrefix, \
+                              args.availability, \
+                              cachedWebfingers,personCache, \
+                              True)
+    for i in range(10):
+        # TODO detect send success/fail
+        time.sleep(1)
+    sys.exit()
+
 if federationList:
     print('Federating with: '+str(federationList))
 

+ 0 - 17
person.py

@@ -106,23 +106,6 @@ def setOrganizationScheme(baseDir: str,nickname: str,domain: str, \
             commentjson.dump(actorJson, fp, indent=4, sort_keys=False)    
     return True
 
-def setAvailability(baseDir: str,nickname: str,domain: str, \
-                    status: str) -> bool:
-    """Set an availability status
-    """
-    # avoid giant strings
-    if len(status)>128:
-        return False
-    actorFilename=baseDir+'/accounts/'+nickname+'@'+domain+'.json'
-    if not os.path.isfile(actorFilename):
-        return False
-    with open(actorFilename, 'r') as fp:
-        actorJson=commentjson.load(fp)
-        actorJson['availability']=status
-        with open(actorFilename, 'w') as fp:
-            commentjson.dump(actorJson, fp, indent=4, sort_keys=False)    
-    return True
-
 def createPersonBase(baseDir: str,nickname: str,domain: str,port: int, \
                      httpPrefix: str, saveToFile: bool,password=None) -> (str,str,{},{}):
     """Returns the private key, public key, actor and webfinger endpoint

+ 4 - 4
skills.py

@@ -69,8 +69,8 @@ def outboxSkills(baseDir: str,nickname: str,messageJson: {},debug: bool) -> bool
     if actorNickname!=nickname:
         return False
     domain,port=getDomainFromActor(messageJson['actor'])
-    skill=messageJson['object'].split(';')[0].strip()
-    skillLevelPercent=messageJson['object'].split(';')[1].strip()
+    skill=messageJson['object'].replace('"','').split(';')[0].strip()
+    skillLevelPercent=int(messageJson['object'].replace('"','').split(';')[1].strip())
 
     return setSkillLevel(baseDir,nickname,domain, \
                          skill,skillLevelPercent)
@@ -92,7 +92,7 @@ def sendSkillViaServer(session,nickname: str,password: str,
         domainFull=domain+':'+str(port)
         
     toUrl = httpPrefix+'://'+domainFull+'/users/'+nickname
-    ccUrl = httpPrefix+'://'+domainFull+'/users/'+Nickname+'/followers'
+    ccUrl = httpPrefix+'://'+domainFull+'/users/'+nickname+'/followers'
 
     if skillLevelPercent:
         skillStr=skill+';'+str(skillLevelPercent)
@@ -101,7 +101,7 @@ def sendSkillViaServer(session,nickname: str,password: str,
     newSkillJson = {
         'type': 'Skill',
         'actor': httpPrefix+'://'+domainFull+'/users/'+nickname,
-        'object': skill+';'+str(skillLevelPercent),
+        'object': '"'+skillStr+'"',
         'to': [toUrl],
         'cc': [ccUrl]
     }