Modern ActivityPub compliant server, designed for simplicity and accessibility. Includes calendar and sharing economy features to empower your federated community. https://code.freedombone.net/bashrc/epicyon Docs: https://epicyon.net/#install
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

skills.py 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. __filename__ = "skills.py"
  2. __author__ = "Bob Mottram"
  3. __license__ = "AGPL3+"
  4. __version__ = "1.2.0"
  5. __maintainer__ = "Bob Mottram"
  6. __email__ = "bob@freedombone.net"
  7. __status__ = "Production"
  8. import os
  9. from webfinger import webfingerHandle
  10. from auth import createBasicAuthHeader
  11. from posts import getPersonBox
  12. from session import postJson
  13. from utils import getFullDomain
  14. from utils import getNicknameFromActor
  15. from utils import getDomainFromActor
  16. from utils import loadJson
  17. def setSkillsFromDict(actorJson: {}, skillsDict: {}) -> str:
  18. """Converts a dict containing skills to a string
  19. Returns the string version of the dictionary
  20. """
  21. skillsStr = ''
  22. for name, value in skillsDict.items():
  23. if skillsStr:
  24. skillsStr += ', '
  25. skillsStr += name + ':' + str(value)
  26. actorJson['hasOccupation']['skills'] = skillsStr
  27. return skillsStr
  28. def getSkillsFromString(skillsStr: str) -> {}:
  29. """Returns a dict of skills from a string
  30. """
  31. skillsList = skillsStr.split(',')
  32. skillsDict = {}
  33. for skill in skillsList:
  34. if ':' not in skill:
  35. continue
  36. name = skill.split(':')[0].strip().lower()
  37. valueStr = skill.split(':')[1]
  38. if not valueStr.isdigit():
  39. continue
  40. skillsDict[name] = int(valueStr)
  41. return skillsDict
  42. def actorHasSkill(actorJson: {}, skillName: str) -> bool:
  43. """Returns true if the actor has the given skill
  44. """
  45. skillsDict = \
  46. getSkillsFromString(actorJson['hasOccupation']['skills'])
  47. if not skillsDict:
  48. return False
  49. return skillsDict.get(skillName.lower())
  50. def actorSkillValue(actorJson: {}, skillName: str) -> int:
  51. """Returns The skill level from an actor
  52. """
  53. skillsDict = \
  54. getSkillsFromString(actorJson['hasOccupation']['skills'])
  55. if not skillsDict:
  56. return 0
  57. skillName = skillName.lower()
  58. if skillsDict.get(skillName):
  59. return skillsDict[skillName]
  60. return 0
  61. def noOfActorSkills(actorJson: {}) -> int:
  62. """Returns the number of skills that an actor has
  63. """
  64. if actorJson.get('hasOccupation'):
  65. skillsList = actorJson['hasOccupation']['skills'].split(',')
  66. if skillsList:
  67. return len(skillsList)
  68. return 0
  69. def setActorSkillLevel(actorJson: {},
  70. skill: str, skillLevelPercent: int) -> bool:
  71. """Set a skill level for a person
  72. Setting skill level to zero removes it
  73. """
  74. if skillLevelPercent < 0 or skillLevelPercent > 100:
  75. return False
  76. if not actorJson:
  77. return True
  78. if not actorJson.get('hasOccupation'):
  79. actorJson['hasOccupation'] = {
  80. '@type': 'Occupation',
  81. 'name': '',
  82. 'skills': ''
  83. }
  84. skillsDict = \
  85. getSkillsFromString(actorJson['hasOccupation']['skills'])
  86. if skillLevelPercent > 0:
  87. skillsDict[skill] = skillLevelPercent
  88. else:
  89. if skillsDict.get(skill):
  90. del skillsDict[skill]
  91. setSkillsFromDict(actorJson, skillsDict)
  92. return True
  93. def setSkillLevel(baseDir: str, nickname: str, domain: str,
  94. skill: str, skillLevelPercent: int) -> bool:
  95. """Set a skill level for a person
  96. Setting skill level to zero removes it
  97. """
  98. if skillLevelPercent < 0 or skillLevelPercent > 100:
  99. return False
  100. actorFilename = baseDir + '/accounts/' + nickname + '@' + domain + '.json'
  101. if not os.path.isfile(actorFilename):
  102. return False
  103. actorJson = loadJson(actorFilename)
  104. return setActorSkillLevel(actorJson,
  105. skill, skillLevelPercent)
  106. def getSkills(baseDir: str, nickname: str, domain: str) -> []:
  107. """Returns the skills for a given person
  108. """
  109. actorFilename = baseDir + '/accounts/' + nickname + '@' + domain + '.json'
  110. if not os.path.isfile(actorFilename):
  111. return False
  112. actorJson = loadJson(actorFilename)
  113. if actorJson:
  114. if not actorJson.get('hasOccupation'):
  115. return None
  116. return getSkillsFromString(actorJson['hasOccupation']['skills'])
  117. return None
  118. def outboxSkills(baseDir: str, nickname: str, messageJson: {},
  119. debug: bool) -> bool:
  120. """Handles receiving a skills update
  121. """
  122. if not messageJson.get('type'):
  123. return False
  124. if not messageJson['type'] == 'Skill':
  125. return False
  126. if not messageJson.get('actor'):
  127. return False
  128. if not messageJson.get('object'):
  129. return False
  130. if not isinstance(messageJson['object'], str):
  131. return False
  132. actorNickname = getNicknameFromActor(messageJson['actor'])
  133. if actorNickname != nickname:
  134. return False
  135. domain, port = getDomainFromActor(messageJson['actor'])
  136. skill = messageJson['object'].replace('"', '').split(';')[0].strip()
  137. skillLevelPercentStr = \
  138. messageJson['object'].replace('"', '').split(';')[1].strip()
  139. skillLevelPercent = 50
  140. if skillLevelPercentStr.isdigit():
  141. skillLevelPercent = int(skillLevelPercentStr)
  142. return setSkillLevel(baseDir, nickname, domain,
  143. skill, skillLevelPercent)
  144. def sendSkillViaServer(baseDir: str, session, nickname: str, password: str,
  145. domain: str, port: int,
  146. httpPrefix: str,
  147. skill: str, skillLevelPercent: int,
  148. cachedWebfingers: {}, personCache: {},
  149. debug: bool, projectVersion: str) -> {}:
  150. """Sets a skill for a person via c2s
  151. """
  152. if not session:
  153. print('WARN: No session for sendSkillViaServer')
  154. return 6
  155. domainFull = getFullDomain(domain, port)
  156. actor = httpPrefix + '://' + domainFull + '/users/' + nickname
  157. toUrl = actor
  158. ccUrl = actor + '/followers'
  159. if skillLevelPercent:
  160. skillStr = skill + ';' + str(skillLevelPercent)
  161. else:
  162. skillStr = skill + ';0'
  163. newSkillJson = {
  164. 'type': 'Skill',
  165. 'actor': actor,
  166. 'object': '"' + skillStr + '"',
  167. 'to': [toUrl],
  168. 'cc': [ccUrl]
  169. }
  170. handle = httpPrefix + '://' + domainFull + '/@' + nickname
  171. # lookup the inbox for the To handle
  172. wfRequest = \
  173. webfingerHandle(session, handle, httpPrefix,
  174. cachedWebfingers,
  175. domain, projectVersion, debug)
  176. if not wfRequest:
  177. if debug:
  178. print('DEBUG: skill webfinger failed for ' + handle)
  179. return 1
  180. if not isinstance(wfRequest, dict):
  181. print('WARN: skill webfinger for ' + handle +
  182. ' did not return a dict. ' + str(wfRequest))
  183. return 1
  184. postToBox = 'outbox'
  185. # get the actor inbox for the To handle
  186. (inboxUrl, pubKeyId, pubKey,
  187. fromPersonId, sharedInbox,
  188. avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest,
  189. personCache, projectVersion,
  190. httpPrefix, nickname, domain,
  191. postToBox, 86725)
  192. if not inboxUrl:
  193. if debug:
  194. print('DEBUG: skill no ' + postToBox +
  195. ' was found for ' + handle)
  196. return 3
  197. if not fromPersonId:
  198. if debug:
  199. print('DEBUG: skill no actor was found for ' + handle)
  200. return 4
  201. authHeader = createBasicAuthHeader(nickname, password)
  202. headers = {
  203. 'host': domain,
  204. 'Content-type': 'application/json',
  205. 'Authorization': authHeader
  206. }
  207. postResult = \
  208. postJson(session, newSkillJson, [], inboxUrl,
  209. headers, 30, True)
  210. if not postResult:
  211. if debug:
  212. print('DEBUG: POST skill failed for c2s to ' + inboxUrl)
  213. # return 5
  214. if debug:
  215. print('DEBUG: c2s POST skill success')
  216. return newSkillJson