mirror of https://gitlab.com/bashrc2/epicyon
Merge branch 'main' of ssh://code.freedombone.net:2222/bashrc/epicyon into main
commit
680e047af2
46
daemon.py
46
daemon.py
|
|
@ -269,6 +269,7 @@ from filters import isFiltered
|
||||||
from filters import addGlobalFilter
|
from filters import addGlobalFilter
|
||||||
from filters import removeGlobalFilter
|
from filters import removeGlobalFilter
|
||||||
from context import hasValidContext
|
from context import hasValidContext
|
||||||
|
from speaker import getSSMLbox
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -487,24 +488,28 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
"""
|
"""
|
||||||
if not self.headers.get('Accept'):
|
if not self.headers.get('Accept'):
|
||||||
return False
|
return False
|
||||||
|
acceptStr = self.headers['Accept']
|
||||||
if self.server.debug:
|
if self.server.debug:
|
||||||
print('ACCEPT: ' + self.headers['Accept'])
|
print('ACCEPT: ' + acceptStr)
|
||||||
if 'image/' in self.headers['Accept']:
|
if 'application/ssml' in acceptStr:
|
||||||
if 'text/html' not in self.headers['Accept']:
|
if 'text/html' not in acceptStr:
|
||||||
return False
|
return False
|
||||||
if 'video/' in self.headers['Accept']:
|
if 'image/' in acceptStr:
|
||||||
if 'text/html' not in self.headers['Accept']:
|
if 'text/html' not in acceptStr:
|
||||||
return False
|
return False
|
||||||
if 'audio/' in self.headers['Accept']:
|
if 'video/' in acceptStr:
|
||||||
if 'text/html' not in self.headers['Accept']:
|
if 'text/html' not in acceptStr:
|
||||||
return False
|
return False
|
||||||
if self.headers['Accept'].startswith('*'):
|
if 'audio/' in acceptStr:
|
||||||
|
if 'text/html' not in acceptStr:
|
||||||
|
return False
|
||||||
|
if acceptStr.startswith('*'):
|
||||||
if self.headers.get('User-Agent'):
|
if self.headers.get('User-Agent'):
|
||||||
if 'ELinks' in self.headers['User-Agent'] or \
|
if 'ELinks' in self.headers['User-Agent'] or \
|
||||||
'Lynx' in self.headers['User-Agent']:
|
'Lynx' in self.headers['User-Agent']:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
if 'json' in self.headers['Accept']:
|
if 'json' in acceptStr:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
@ -10480,10 +10485,25 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
# arriving in your inbox
|
# arriving in your inbox
|
||||||
if authorized and usersInPath and \
|
if authorized and usersInPath and \
|
||||||
self.path.endswith('/speaker'):
|
self.path.endswith('/speaker'):
|
||||||
self._getSpeaker(callingDomain, self.path,
|
if 'application/ssml' not in self.headers['Accept']:
|
||||||
self.server.baseDir,
|
# json endpoint
|
||||||
self.server.domain,
|
self._getSpeaker(callingDomain, self.path,
|
||||||
self.server.debug)
|
self.server.baseDir,
|
||||||
|
self.server.domain,
|
||||||
|
self.server.debug)
|
||||||
|
else:
|
||||||
|
xmlStr = \
|
||||||
|
getSSMLbox(self.server.baseDir,
|
||||||
|
self.path, self.server.domain,
|
||||||
|
self.server.systemLanguage,
|
||||||
|
self.server.instanceTitle,
|
||||||
|
'inbox')
|
||||||
|
if xmlStr:
|
||||||
|
msg = xmlStr.encode('utf-8')
|
||||||
|
msglen = len(msg)
|
||||||
|
self._set_headers('application/xrd+xml', msglen,
|
||||||
|
None, callingDomain)
|
||||||
|
self._write(msg)
|
||||||
return
|
return
|
||||||
|
|
||||||
# redirect to the welcome screen
|
# redirect to the welcome screen
|
||||||
|
|
|
||||||
14
inbox.py
14
inbox.py
|
|
@ -14,6 +14,7 @@ import html
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from linked_data_sig import verifyJsonSignature
|
from linked_data_sig import verifyJsonSignature
|
||||||
from utils import getDisplayName
|
from utils import getDisplayName
|
||||||
|
from utils import getGenderFromBio
|
||||||
from utils import removeHtml
|
from utils import removeHtml
|
||||||
from utils import getConfigParam
|
from utils import getConfigParam
|
||||||
from utils import hasUsersPath
|
from utils import hasUsersPath
|
||||||
|
|
@ -84,6 +85,7 @@ from context import hasValidContext
|
||||||
from content import htmlReplaceQuoteMarks
|
from content import htmlReplaceQuoteMarks
|
||||||
from speaker import speakerReplaceLinks
|
from speaker import speakerReplaceLinks
|
||||||
from speaker import speakerPronounce
|
from speaker import speakerPronounce
|
||||||
|
from speaker import speakerEndpointJson
|
||||||
|
|
||||||
|
|
||||||
def storeHashTags(baseDir: str, nickname: str, postJsonObject: {}) -> None:
|
def storeHashTags(baseDir: str, nickname: str, postJsonObject: {}) -> None:
|
||||||
|
|
@ -2193,6 +2195,8 @@ def _updateSpeaker(baseDir: str, nickname: str, domain: str,
|
||||||
|
|
||||||
speakerName = \
|
speakerName = \
|
||||||
getDisplayName(baseDir, postJsonObject['actor'], personCache)
|
getDisplayName(baseDir, postJsonObject['actor'], personCache)
|
||||||
|
gender = getGenderFromBio(baseDir, postJsonObject['actor'],
|
||||||
|
personCache, translate)
|
||||||
if not speakerName:
|
if not speakerName:
|
||||||
return
|
return
|
||||||
if announcingActor:
|
if announcingActor:
|
||||||
|
|
@ -2201,13 +2205,9 @@ def _updateSpeaker(baseDir: str, nickname: str, domain: str,
|
||||||
announcedHandle = announcedNickname + '@' + announcedDomain
|
announcedHandle = announcedNickname + '@' + announcedDomain
|
||||||
content = \
|
content = \
|
||||||
translate['announces'] + ' ' + announcedHandle + '. ' + content
|
translate['announces'] + ' ' + announcedHandle + '. ' + content
|
||||||
speakerJson = {
|
speakerJson = speakerEndpointJson(speakerName, summary,
|
||||||
"name": speakerName,
|
content, imageDescription,
|
||||||
"summary": summary,
|
detectedLinks, gender)
|
||||||
"say": content,
|
|
||||||
"imageDescription": imageDescription,
|
|
||||||
"detectedLinks": detectedLinks
|
|
||||||
}
|
|
||||||
saveJson(speakerJson, speakerFilename)
|
saveJson(speakerJson, speakerFilename)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -276,12 +276,13 @@ def _createPersonBase(baseDir: str, nickname: str, domain: str, port: int,
|
||||||
'devices': personId + '/collections/devices',
|
'devices': personId + '/collections/devices',
|
||||||
'endpoints': {
|
'endpoints': {
|
||||||
'id': personId + '/endpoints',
|
'id': personId + '/endpoints',
|
||||||
'sharedInbox': httpPrefix+'://' + domain + '/inbox',
|
'sharedInbox': httpPrefix + '://' + domain + '/inbox',
|
||||||
},
|
},
|
||||||
'featured': personId + '/collections/featured',
|
'featured': personId + '/collections/featured',
|
||||||
'featuredTags': personId + '/collections/tags',
|
'featuredTags': personId + '/collections/tags',
|
||||||
'followers': personId + '/followers',
|
'followers': personId + '/followers',
|
||||||
'following': personId + '/following',
|
'following': personId + '/following',
|
||||||
|
'tts': personId + '/speaker',
|
||||||
'shares': personId + '/shares',
|
'shares': personId + '/shares',
|
||||||
'orgSchema': None,
|
'orgSchema': None,
|
||||||
'skills': {},
|
'skills': {},
|
||||||
|
|
@ -556,6 +557,10 @@ def personUpgradeActor(baseDir: str, personJson: {},
|
||||||
personJson = loadJson(filename)
|
personJson = loadJson(filename)
|
||||||
|
|
||||||
if updateActor:
|
if updateActor:
|
||||||
|
# add a speaker endpoint
|
||||||
|
if not personJson.get('tts'):
|
||||||
|
personJson['tts'] = personJson['id'] + '/speaker'
|
||||||
|
|
||||||
saveJson(personJson, filename)
|
saveJson(personJson, filename)
|
||||||
|
|
||||||
# also update the actor within the cache
|
# also update the actor within the cache
|
||||||
|
|
|
||||||
120
speaker.py
120
speaker.py
|
|
@ -10,8 +10,11 @@ import os
|
||||||
import random
|
import random
|
||||||
from auth import createBasicAuthHeader
|
from auth import createBasicAuthHeader
|
||||||
from session import getJson
|
from session import getJson
|
||||||
|
from utils import loadJson
|
||||||
from utils import getFullDomain
|
from utils import getFullDomain
|
||||||
|
|
||||||
|
speakerRemoveChars = ('.\n', '. ', ',', ';', '?', '!')
|
||||||
|
|
||||||
|
|
||||||
def getSpeakerPitch(displayName: str, screenreader: str) -> int:
|
def getSpeakerPitch(displayName: str, screenreader: str) -> int:
|
||||||
"""Returns the speech synthesis pitch for the given name
|
"""Returns the speech synthesis pitch for the given name
|
||||||
|
|
@ -96,9 +99,8 @@ def speakerReplaceLinks(sayText: str, translate: {},
|
||||||
"""Replaces any links in the given text with "link to [domain]".
|
"""Replaces any links in the given text with "link to [domain]".
|
||||||
Instead of reading out potentially very long and meaningless links
|
Instead of reading out potentially very long and meaningless links
|
||||||
"""
|
"""
|
||||||
removeChars = ('.\n', '. ', ',', ';', '?', '!')
|
|
||||||
text = sayText
|
text = sayText
|
||||||
for ch in removeChars:
|
for ch in speakerRemoveChars:
|
||||||
text = text.replace(ch, ' ')
|
text = text.replace(ch, ' ')
|
||||||
replacements = {}
|
replacements = {}
|
||||||
wordsList = text.split(' ')
|
wordsList = text.split(' ')
|
||||||
|
|
@ -136,6 +138,28 @@ def speakerReplaceLinks(sayText: str, translate: {},
|
||||||
return sayText.replace('..', '.')
|
return sayText.replace('..', '.')
|
||||||
|
|
||||||
|
|
||||||
|
def _addSSMLemphasis(sayText: str) -> str:
|
||||||
|
"""Adds emphasis to *emphasised* text
|
||||||
|
"""
|
||||||
|
if '*' not in sayText:
|
||||||
|
return sayText
|
||||||
|
text = sayText
|
||||||
|
for ch in speakerRemoveChars:
|
||||||
|
text = text.replace(ch, ' ')
|
||||||
|
wordsList = text.split(' ')
|
||||||
|
replacements = {}
|
||||||
|
for word in wordsList:
|
||||||
|
if word.startswith('*'):
|
||||||
|
if word.endswith('*'):
|
||||||
|
replacements[word] = \
|
||||||
|
'<emphasis level="strong">' + \
|
||||||
|
word.replace('*', '') + \
|
||||||
|
'</emphasis>'
|
||||||
|
for replaceStr, newStr in replacements.items():
|
||||||
|
sayText = sayText.replace(replaceStr, newStr)
|
||||||
|
return sayText
|
||||||
|
|
||||||
|
|
||||||
def getSpeakerFromServer(baseDir: str, session,
|
def getSpeakerFromServer(baseDir: str, session,
|
||||||
nickname: str, password: str,
|
nickname: str, password: str,
|
||||||
domain: str, port: int,
|
domain: str, port: int,
|
||||||
|
|
@ -166,3 +190,95 @@ def getSpeakerFromServer(baseDir: str, session,
|
||||||
getJson(session, url, headers, None,
|
getJson(session, url, headers, None,
|
||||||
__version__, httpPrefix, domain)
|
__version__, httpPrefix, domain)
|
||||||
return speakerJson
|
return speakerJson
|
||||||
|
|
||||||
|
|
||||||
|
def speakerEndpointJson(displayName: str, summary: str,
|
||||||
|
content: str, imageDescription: str,
|
||||||
|
links: [], gender: str) -> {}:
|
||||||
|
"""Returns a json endpoint for the TTS speaker
|
||||||
|
"""
|
||||||
|
speakerJson = {
|
||||||
|
"name": displayName,
|
||||||
|
"summary": summary,
|
||||||
|
"say": content,
|
||||||
|
"imageDescription": imageDescription,
|
||||||
|
"detectedLinks": links
|
||||||
|
}
|
||||||
|
if gender:
|
||||||
|
speakerJson['gender'] = gender
|
||||||
|
return speakerJson
|
||||||
|
|
||||||
|
|
||||||
|
def _speakerEndpointSSML(displayName: str, summary: str,
|
||||||
|
content: str, imageDescription: str,
|
||||||
|
links: [], language: str,
|
||||||
|
instanceTitle: str,
|
||||||
|
gender: str) -> str:
|
||||||
|
"""Returns an SSML endpoint for the TTS speaker
|
||||||
|
https://en.wikipedia.org/wiki/Speech_Synthesis_Markup_Language
|
||||||
|
https://www.w3.org/TR/speech-synthesis/
|
||||||
|
"""
|
||||||
|
langShort = 'en'
|
||||||
|
if language:
|
||||||
|
langShort = language[:2]
|
||||||
|
if not gender:
|
||||||
|
gender = 'neutral'
|
||||||
|
else:
|
||||||
|
if langShort == 'en':
|
||||||
|
gender = gender.lower()
|
||||||
|
if 'he/him' in gender:
|
||||||
|
gender = 'male'
|
||||||
|
elif 'she/her' in gender:
|
||||||
|
gender = 'female'
|
||||||
|
else:
|
||||||
|
gender = 'neutral'
|
||||||
|
|
||||||
|
content = _addSSMLemphasis(content)
|
||||||
|
voiceParams = 'name="' + displayName + '" gender="' + gender + '"'
|
||||||
|
return '<?xml version="1.0"?>\n' + \
|
||||||
|
'<speak xmlns="http://www.w3.org/2001/10/synthesis"\n' + \
|
||||||
|
' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n' + \
|
||||||
|
' xsi:schemaLocation="http://www.w3.org/2001/10/synthesis\n' + \
|
||||||
|
' http://www.w3.org/TR/speech-synthesis11/synthesis.xsd"\n' + \
|
||||||
|
' version="1.1">\n' + \
|
||||||
|
' <metadata>\n' + \
|
||||||
|
' <dc:title xml:lang="' + langShort + '">' + \
|
||||||
|
instanceTitle + ' inbox</dc:title>\n' + \
|
||||||
|
' </metadata>\n' + \
|
||||||
|
' <p>\n' + \
|
||||||
|
' <s xml:lang="' + language + '">\n' + \
|
||||||
|
' <voice ' + voiceParams + '>\n' + \
|
||||||
|
' ' + content + '\n' + \
|
||||||
|
' </voice>\n' + \
|
||||||
|
' </s>\n' + \
|
||||||
|
' </p>\n' + \
|
||||||
|
'</speak>\n'
|
||||||
|
|
||||||
|
|
||||||
|
def getSSMLbox(baseDir: str, path: str,
|
||||||
|
domain: str,
|
||||||
|
systemLanguage: str,
|
||||||
|
instanceTitle: str,
|
||||||
|
boxName: str) -> str:
|
||||||
|
"""Returns SSML for the given timeline
|
||||||
|
"""
|
||||||
|
nickname = path.split('/users/')[1]
|
||||||
|
if '/' in nickname:
|
||||||
|
nickname = nickname.split('/')[0]
|
||||||
|
speakerFilename = \
|
||||||
|
baseDir + '/accounts/' + nickname + '@' + domain + '/speaker.json'
|
||||||
|
if not os.path.isfile(speakerFilename):
|
||||||
|
return None
|
||||||
|
speakerJson = loadJson(speakerFilename)
|
||||||
|
if not speakerJson:
|
||||||
|
return None
|
||||||
|
gender = None
|
||||||
|
if speakerJson.get('gender'):
|
||||||
|
gender = speakerJson['gender']
|
||||||
|
return _speakerEndpointSSML(speakerJson['name'],
|
||||||
|
speakerJson['summary'],
|
||||||
|
speakerJson['say'],
|
||||||
|
speakerJson['imageDescription'],
|
||||||
|
speakerJson['detectedLinks'],
|
||||||
|
systemLanguage,
|
||||||
|
instanceTitle, gender)
|
||||||
|
|
|
||||||
7
theme.py
7
theme.py
|
|
@ -68,8 +68,9 @@ def _copyThemeHelpFiles(baseDir: str, themeName: str,
|
||||||
if destHelpMarkdownFile == 'profile.md' or \
|
if destHelpMarkdownFile == 'profile.md' or \
|
||||||
destHelpMarkdownFile == 'final.md':
|
destHelpMarkdownFile == 'final.md':
|
||||||
destHelpMarkdownFile = 'welcome_' + destHelpMarkdownFile
|
destHelpMarkdownFile = 'welcome_' + destHelpMarkdownFile
|
||||||
copyfile(themeDir + '/' + helpMarkdownFile,
|
if os.path.isdir(baseDir + '/accounts'):
|
||||||
baseDir + '/accounts/' + destHelpMarkdownFile)
|
copyfile(themeDir + '/' + helpMarkdownFile,
|
||||||
|
baseDir + '/accounts/' + destHelpMarkdownFile)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -659,6 +660,8 @@ def _setClearCacheFlag(baseDir: str) -> None:
|
||||||
"""Sets a flag which can be used by an external system
|
"""Sets a flag which can be used by an external system
|
||||||
(eg. a script in a cron job) to clear the browser cache
|
(eg. a script in a cron job) to clear the browser cache
|
||||||
"""
|
"""
|
||||||
|
if not os.path.isdir(baseDir + '/accounts'):
|
||||||
|
return
|
||||||
flagFilename = baseDir + '/accounts/.clear_cache'
|
flagFilename = baseDir + '/accounts/.clear_cache'
|
||||||
with open(flagFilename, 'w+') as flagFile:
|
with open(flagFilename, 'w+') as flagFile:
|
||||||
flagFile.write('\n')
|
flagFile.write('\n')
|
||||||
|
|
|
||||||
|
|
@ -381,5 +381,8 @@
|
||||||
"mentioning": "ذكر",
|
"mentioning": "ذكر",
|
||||||
"sad face": "وجه حزين",
|
"sad face": "وجه حزين",
|
||||||
"thinking emoji": "التفكير الرموز التعبيرية",
|
"thinking emoji": "التفكير الرموز التعبيرية",
|
||||||
"laughing": "يضحك"
|
"laughing": "يضحك",
|
||||||
|
"gender": "جنس تذكير أو تأنيث",
|
||||||
|
"He/Him": "هو",
|
||||||
|
"She/Her": "هي"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,5 +381,8 @@
|
||||||
"mentioning": "esmentant",
|
"mentioning": "esmentant",
|
||||||
"sad face": "cara trista",
|
"sad face": "cara trista",
|
||||||
"thinking emoji": "emoji pensant",
|
"thinking emoji": "emoji pensant",
|
||||||
"laughing": "rient"
|
"laughing": "rient",
|
||||||
|
"gender": "gènere",
|
||||||
|
"He/Him": "Ell",
|
||||||
|
"She/Her": "Ella"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,5 +381,8 @@
|
||||||
"mentioning": "sôn",
|
"mentioning": "sôn",
|
||||||
"sad face": "wyneb trist",
|
"sad face": "wyneb trist",
|
||||||
"thinking emoji": "meddwl emoji",
|
"thinking emoji": "meddwl emoji",
|
||||||
"laughing": "chwerthin"
|
"laughing": "chwerthin",
|
||||||
|
"gender": "rhyw",
|
||||||
|
"He/Him": "Ef",
|
||||||
|
"She/Her": "Hi/Ei"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,5 +381,8 @@
|
||||||
"mentioning": "Erwähnen",
|
"mentioning": "Erwähnen",
|
||||||
"sad face": "trauriges Gesicht",
|
"sad face": "trauriges Gesicht",
|
||||||
"thinking emoji": "Emowji denken",
|
"thinking emoji": "Emowji denken",
|
||||||
"laughing": "Lachen"
|
"laughing": "Lachen",
|
||||||
|
"gender": "geschlecht",
|
||||||
|
"He/Him": "Er/ihm",
|
||||||
|
"She/Her": "Sie"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,5 +381,8 @@
|
||||||
"mentioning": "mentioning",
|
"mentioning": "mentioning",
|
||||||
"sad face": "sad face",
|
"sad face": "sad face",
|
||||||
"thinking emoji": "thinking emowji",
|
"thinking emoji": "thinking emowji",
|
||||||
"laughing": "laughing"
|
"laughing": "laughing",
|
||||||
|
"gender": "gender",
|
||||||
|
"He/Him": "He/Him",
|
||||||
|
"She/Her": "She/Her"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,5 +381,8 @@
|
||||||
"mentioning": "mencionar",
|
"mentioning": "mencionar",
|
||||||
"sad face": "cara triste",
|
"sad face": "cara triste",
|
||||||
"thinking emoji": "pensando emowji",
|
"thinking emoji": "pensando emowji",
|
||||||
"laughing": "risa"
|
"laughing": "risa",
|
||||||
|
"gender": "género",
|
||||||
|
"He/Him": "El",
|
||||||
|
"She/Her": "Ella"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,5 +381,8 @@
|
||||||
"mentioning": "mentionnant",
|
"mentioning": "mentionnant",
|
||||||
"sad face": "visage triste",
|
"sad face": "visage triste",
|
||||||
"thinking emoji": "penser emowji",
|
"thinking emoji": "penser emowji",
|
||||||
"laughing": "en riant"
|
"laughing": "en riant",
|
||||||
|
"gender": "le genre",
|
||||||
|
"He/Him": "Il/Lui",
|
||||||
|
"She/Her": "Elle"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,5 +381,8 @@
|
||||||
"mentioning": "ag lua",
|
"mentioning": "ag lua",
|
||||||
"sad face": "aghaidh brónach",
|
"sad face": "aghaidh brónach",
|
||||||
"thinking emoji": "ag smaoineamh emowji",
|
"thinking emoji": "ag smaoineamh emowji",
|
||||||
"laughing": "ag gáire"
|
"laughing": "ag gáire",
|
||||||
|
"gender": "inscne",
|
||||||
|
"He/Him": "Sé/Eisean",
|
||||||
|
"She/Her": "Sí"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,5 +381,8 @@
|
||||||
"mentioning": "उल्लेख",
|
"mentioning": "उल्लेख",
|
||||||
"sad face": "उदास चेहरा",
|
"sad face": "उदास चेहरा",
|
||||||
"thinking emoji": "सोच रहे हैं इमोजी",
|
"thinking emoji": "सोच रहे हैं इमोजी",
|
||||||
"laughing": "हस रहा"
|
"laughing": "हस रहा",
|
||||||
|
"gender": "लिंग",
|
||||||
|
"He/Him": "वह/उसे",
|
||||||
|
"She/Her": "वह/उसकी"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,5 +381,8 @@
|
||||||
"mentioning": "menzionando",
|
"mentioning": "menzionando",
|
||||||
"sad face": "faccia triste",
|
"sad face": "faccia triste",
|
||||||
"thinking emoji": "pensiero emoji",
|
"thinking emoji": "pensiero emoji",
|
||||||
"laughing": "ridendo"
|
"laughing": "ridendo",
|
||||||
|
"gender": "genere",
|
||||||
|
"He/Him": "Lui",
|
||||||
|
"She/Her": "Lei"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,5 +381,8 @@
|
||||||
"mentioning": "言及する",
|
"mentioning": "言及する",
|
||||||
"sad face": "悲しい顔",
|
"sad face": "悲しい顔",
|
||||||
"thinking emoji": "絵文字を考える",
|
"thinking emoji": "絵文字を考える",
|
||||||
"laughing": "笑い"
|
"laughing": "笑い",
|
||||||
|
"gender": "性別",
|
||||||
|
"He/Him": "彼",
|
||||||
|
"She/Her": "彼女"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -377,5 +377,8 @@
|
||||||
"mentioning": "mentioning",
|
"mentioning": "mentioning",
|
||||||
"sad face": "sad face",
|
"sad face": "sad face",
|
||||||
"thinking emoji": "thinking emowji",
|
"thinking emoji": "thinking emowji",
|
||||||
"laughing": "laughing"
|
"laughing": "laughing",
|
||||||
|
"gender": "gender",
|
||||||
|
"He/Him": "He/Him",
|
||||||
|
"She/Her": "She/Her"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,5 +381,8 @@
|
||||||
"mentioning": "mencionando",
|
"mentioning": "mencionando",
|
||||||
"sad face": "rosto triste",
|
"sad face": "rosto triste",
|
||||||
"thinking emoji": "pensando emowji",
|
"thinking emoji": "pensando emowji",
|
||||||
"laughing": "rindo"
|
"laughing": "rindo",
|
||||||
|
"gender": "gênero",
|
||||||
|
"He/Him": "Ele",
|
||||||
|
"She/Her": "Ela"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,5 +381,8 @@
|
||||||
"mentioning": "упоминание",
|
"mentioning": "упоминание",
|
||||||
"sad face": "грустное лицо",
|
"sad face": "грустное лицо",
|
||||||
"thinking emoji": "думающий смайлик",
|
"thinking emoji": "думающий смайлик",
|
||||||
"laughing": "смеющийся"
|
"laughing": "смеющийся",
|
||||||
|
"gender": "Пол",
|
||||||
|
"He/Him": "Он/Его",
|
||||||
|
"She/Her": "Она/Ее"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,5 +381,8 @@
|
||||||
"mentioning": "提及",
|
"mentioning": "提及",
|
||||||
"sad face": "悲伤的脸",
|
"sad face": "悲伤的脸",
|
||||||
"thinking emoji": "思维表情符号",
|
"thinking emoji": "思维表情符号",
|
||||||
"laughing": "笑"
|
"laughing": "笑",
|
||||||
|
"gender": "",
|
||||||
|
"He/Him": "",
|
||||||
|
"She/Her": ""
|
||||||
}
|
}
|
||||||
|
|
|
||||||
68
utils.py
68
utils.py
|
|
@ -669,6 +669,74 @@ def getDisplayName(baseDir: str, actor: str, personCache: {}) -> str:
|
||||||
return nameFound
|
return nameFound
|
||||||
|
|
||||||
|
|
||||||
|
def getGenderFromBio(baseDir: str, actor: str, personCache: {},
|
||||||
|
translate: {}) -> str:
|
||||||
|
"""Tries to ascertain gender from bio description
|
||||||
|
"""
|
||||||
|
if '/statuses/' in actor:
|
||||||
|
actor = actor.split('/statuses/')[0]
|
||||||
|
if not personCache.get(actor):
|
||||||
|
return None
|
||||||
|
bioFound = None
|
||||||
|
if personCache[actor].get('actor'):
|
||||||
|
# is gender defined as a profile tag?
|
||||||
|
if personCache[actor]['actor'].get('attachment'):
|
||||||
|
tagsList = personCache[actor]['actor']['attachment']
|
||||||
|
if isinstance(tagsList, list):
|
||||||
|
for tag in tagsList:
|
||||||
|
if not isinstance(tag, dict):
|
||||||
|
continue
|
||||||
|
if not tag.get('name') or not tag.get('value'):
|
||||||
|
continue
|
||||||
|
if tag['name'].lower() == \
|
||||||
|
translate['gender'].lower():
|
||||||
|
bioFound = tag['value']
|
||||||
|
break
|
||||||
|
# if not then use the bio
|
||||||
|
if not bioFound and personCache[actor]['actor'].get('summary'):
|
||||||
|
bioFound = personCache[actor]['actor']['summary']
|
||||||
|
else:
|
||||||
|
# Try to obtain from the cached actors
|
||||||
|
cachedActorFilename = \
|
||||||
|
baseDir + '/cache/actors/' + (actor.replace('/', '#')) + '.json'
|
||||||
|
if os.path.isfile(cachedActorFilename):
|
||||||
|
actorJson = loadJson(cachedActorFilename, 1)
|
||||||
|
if actorJson:
|
||||||
|
# is gender defined as a profile tag?
|
||||||
|
if actorJson.get('attachment'):
|
||||||
|
tagsList = actorJson['attachment']
|
||||||
|
if isinstance(tagsList, list):
|
||||||
|
for tag in tagsList:
|
||||||
|
if not isinstance(tag, dict):
|
||||||
|
continue
|
||||||
|
if not tag.get('name') or not tag.get('value'):
|
||||||
|
continue
|
||||||
|
if tag['name'].lower() == \
|
||||||
|
translate['gender'].lower():
|
||||||
|
bioFound = tag['value']
|
||||||
|
break
|
||||||
|
# if not then use the bio
|
||||||
|
if not bioFound and actorJson.get('summary'):
|
||||||
|
bioFound = actorJson['summary']
|
||||||
|
if not bioFound:
|
||||||
|
return None
|
||||||
|
gender = 'They/Them'
|
||||||
|
bioFoundOrig = bioFound
|
||||||
|
bioFound = bioFound.lower()
|
||||||
|
if translate['He/Him'] in bioFound:
|
||||||
|
gender = 'He/Him'
|
||||||
|
elif translate['She/Her'] in bioFound:
|
||||||
|
gender = 'She/Her'
|
||||||
|
elif 'him' in bioFound or 'male' in bioFound:
|
||||||
|
gender = 'He/Him'
|
||||||
|
elif 'her' in bioFound or 'she' in bioFound or \
|
||||||
|
'fem' in bioFound or 'woman' in bioFound:
|
||||||
|
gender = 'She/Her'
|
||||||
|
elif 'man' in bioFound or 'He' in bioFoundOrig:
|
||||||
|
gender = 'He/Him'
|
||||||
|
return gender
|
||||||
|
|
||||||
|
|
||||||
def getNicknameFromActor(actor: str) -> str:
|
def getNicknameFromActor(actor: str) -> str:
|
||||||
"""Returns the nickname from an actor url
|
"""Returns the nickname from an actor url
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue