merge-requests/30/head
Bob Mottram 2021-10-20 21:51:19 +01:00
commit 0606ca4a3d
28 changed files with 620 additions and 430 deletions

397
daemon.py

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -22,7 +22,7 @@ body, table, input, select, textarea {
.graph {
display:block;
width:600px;
width:100%;
height:300px;
}
@ -63,12 +63,12 @@ body, table, input, select, textarea {
}
.graph tbody:before {
content:"100%";
content:"Max";
top:-0.6em;
}
.graph tbody:after {
content:"0%";
content:"0";
bottom:-0.6em;
}

View File

@ -58,7 +58,13 @@ def sortedWatchPoints(fitness: {}, fitnessId: str) -> []:
if not item.get('total'):
continue
averageTime = item['total'] * 1000 / item['ctr']
result.append(str(averageTime) + ' ' + watchPoint)
averageTimeStr = str(averageTime)
threshold = 10
while threshold < 100000:
if averageTime < threshold:
averageTimeStr = '0' + averageTimeStr
threshold *= 10
result.append(averageTimeStr + ' ' + watchPoint)
result.sort(reverse=True)
return result
@ -97,7 +103,7 @@ def htmlWatchPointsGraph(baseDir: str, fitness: {}, fitnessId: str,
ctr = 0
for watchPoint in watchPointsList:
name = watchPoint.split(' ')[1]
name = watchPoint.split(' ', 1)[1]
averageTime = float(watchPoint.split(' ')[0])
heightPercent = int(averageTime * 100 / maxAverageTime)
timeMS = int(averageTime)

View File

@ -15,6 +15,7 @@ import random
from linked_data_sig import verifyJsonSignature
from languages import understoodPostLanguage
from like import updateLikesCollection
from utils import fileLastModified
from utils import hasObjectString
from utils import hasObjectStringObject
from utils import getReplyIntervalHours
@ -105,6 +106,7 @@ from announce import createAnnounce
from notifyOnPost import notifyWhenPersonPosts
from conversation import updateConversation
from content import validHashTag
from webapp_hashtagswarm import htmlHashTagSwarm
def _storeLastPostId(baseDir: str, nickname: str, domain: str,
@ -138,7 +140,55 @@ def _storeLastPostId(baseDir: str, nickname: str, domain: str,
pass
def storeHashTags(baseDir: str, nickname: str, postJsonObject: {}) -> None:
def _updateCachedHashtagSwarm(baseDir: str, nickname: str, domain: str,
httpPrefix: str, domainFull: str,
translate: {}) -> bool:
"""Updates the hashtag swarm stored as a file
"""
cachedHashtagSwarmFilename = \
acctDir(baseDir, nickname, domain) + '/.hashtagSwarm'
saveSwarm = True
if os.path.isfile(cachedHashtagSwarmFilename):
lastModified = fileLastModified(cachedHashtagSwarmFilename)
modifiedDate = None
try:
modifiedDate = \
datetime.datetime.strptime(lastModified, "%Y-%m-%dT%H:%M:%SZ")
except BaseException:
print('WARN: unable to parse last modified cache date ' +
str(lastModified))
pass
if modifiedDate:
currDate = datetime.datetime.utcnow()
timeDiff = currDate - modifiedDate
diffMins = int(timeDiff.total_seconds() / 60)
if diffMins < 10:
# was saved recently, so don't save again
# This avoids too much disk I/O
saveSwarm = False
else:
print('Updating cached hashtag swarm, last changed ' +
str(diffMins) + ' minutes ago')
else:
print('WARN: no modified date for ' + str(lastModified))
if saveSwarm:
actor = httpPrefix + '://' + domainFull + '/users/' + nickname
newSwarmStr = htmlHashTagSwarm(baseDir, actor, translate)
if newSwarmStr:
try:
with open(cachedHashtagSwarmFilename, 'w+') as fp:
fp.write(newSwarmStr)
return True
except BaseException:
print('WARN: unable to write cached hashtag swarm ' +
cachedHashtagSwarmFilename)
pass
return False
def storeHashTags(baseDir: str, nickname: str, domain: str,
httpPrefix: str, domainFull: str,
postJsonObject: {}, translate: {}) -> None:
"""Extracts hashtags from an incoming post and updates the
relevant tags files.
"""
@ -161,6 +211,7 @@ def storeHashTags(baseDir: str, nickname: str, postJsonObject: {}) -> None:
hashtagCategories = getHashtagCategories(baseDir)
hashtagsCtr = 0
for tag in postJsonObject['object']['tag']:
if not tag.get('type'):
continue
@ -179,6 +230,7 @@ def storeHashTags(baseDir: str, nickname: str, postJsonObject: {}) -> None:
daysDiff = datetime.datetime.utcnow() - datetime.datetime(1970, 1, 1)
daysSinceEpoch = daysDiff.days
tagline = str(daysSinceEpoch) + ' ' + nickname + ' ' + postUrl + '\n'
hashtagsCtr += 1
if not os.path.isfile(tagsFilename):
with open(tagsFilename, 'w+') as tagsFile:
tagsFile.write(tagline)
@ -203,6 +255,12 @@ def storeHashTags(baseDir: str, nickname: str, postJsonObject: {}) -> None:
if categoryStr:
setHashtagCategory(baseDir, tagName, categoryStr, False)
# if some hashtags were found then recalculate the swarm
# ready for later display
if hashtagsCtr > 0:
_updateCachedHashtagSwarm(baseDir, nickname, domain,
httpPrefix, domainFull, translate)
def _inboxStorePostToHtmlCache(recentPostsCache: {}, maxRecentPosts: int,
translate: {},
@ -1594,7 +1652,9 @@ def _receiveAnnounce(recentPostsCache: {},
if debug:
print('DEBUG: Announce post downloaded for ' +
messageJson['actor'] + ' -> ' + messageJson['object'])
storeHashTags(baseDir, nickname, postJsonObject)
storeHashTags(baseDir, nickname, domain,
httpPrefix, domainFull,
postJsonObject, translate)
# Try to obtain the actor for this person
# so that their avatar can be shown
lookupActor = None
@ -2925,7 +2985,9 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int,
_inboxUpdateCalendar(baseDir, handle, postJsonObject)
storeHashTags(baseDir, handleName, postJsonObject)
storeHashTags(baseDir, handleName, domain,
httpPrefix, domainFull,
postJsonObject, translate)
# send the post out to group members
if isGroup:

View File

@ -281,7 +281,8 @@ def hashtagRuleTree(operators: [],
def _hashtagAdd(baseDir: str, httpPrefix: str, domainFull: str,
postJsonObject: {},
actionStr: str, hashtags: [], systemLanguage: str) -> None:
actionStr: str, hashtags: [], systemLanguage: str,
translate: {}) -> None:
"""Adds a hashtag via a hashtag rule
"""
addHashtag = actionStr.split('add ', 1)[1].strip()
@ -326,7 +327,12 @@ def _hashtagAdd(baseDir: str, httpPrefix: str, domainFull: str,
else:
content += hashtagHtml
postJsonObject['object']['content'] = content
storeHashTags(baseDir, 'news', postJsonObject)
domain = domainFull
if ':' in domain:
domain = domain.split(':')[0]
storeHashTags(baseDir, 'news', domain,
httpPrefix, domainFull,
postJsonObject, translate)
def _hashtagRemove(httpPrefix: str, domainFull: str, postJsonObject: {},
@ -369,7 +375,8 @@ def _newswireHashtagProcessing(session, baseDir: str, postJsonObject: {},
federationList: [],
sendThreads: [], postLog: [],
moderated: bool, url: str,
systemLanguage: str) -> bool:
systemLanguage: str,
translate: {}) -> bool:
"""Applies hashtag rules to a news post.
Returns true if the post should be saved to the news timeline
of this instance
@ -413,7 +420,8 @@ def _newswireHashtagProcessing(session, baseDir: str, postJsonObject: {},
if actionStr.startswith('add '):
# add a hashtag
_hashtagAdd(baseDir, httpPrefix, domainFull,
postJsonObject, actionStr, hashtags, systemLanguage)
postJsonObject, actionStr, hashtags, systemLanguage,
translate)
elif actionStr.startswith('remove '):
# remove a hashtag
_hashtagRemove(httpPrefix, domainFull, postJsonObject,
@ -659,7 +667,8 @@ def _convertRSStoActivityPub(baseDir: str, httpPrefix: str,
personCache, cachedWebfingers,
federationList,
sendThreads, postLog,
moderated, url, systemLanguage)
moderated, url, systemLanguage,
translate)
# save the post and update the index
if savePost:
@ -698,7 +707,9 @@ def _convertRSStoActivityPub(baseDir: str, httpPrefix: str,
if tag not in newswire[originalDateStr][6]:
newswire[originalDateStr][6].append(tag)
storeHashTags(baseDir, 'news', blog)
storeHashTags(baseDir, 'news', domain,
httpPrefix, domainFull,
blog, translate)
clearFromPostCaches(baseDir, recentPostsCache, postId)
if saveJson(blog, filename):

View File

@ -15,7 +15,7 @@ from posts import isImageMedia
from posts import outboxMessageCreateWrap
from posts import savePostToBox
from posts import sendToFollowersThread
from posts import sendToNamedAddresses
from posts import sendToNamedAddressesThread
from utils import hasObjectStringType
from utils import getBaseContentFromPost
from utils import hasObjectDict
@ -627,18 +627,20 @@ def postMessageToOutbox(session, translate: {},
else:
print('c2s sender: ' +
postToNickname + '@' + domain + ':' + str(port))
sendToNamedAddresses(server.session, baseDir,
postToNickname,
domain, onionDomain, i2pDomain, port,
httpPrefix,
federationList,
sendThreads,
postLog,
cachedWebfingers,
personCache,
messageJson, debug,
version,
sharedItemsFederatedDomains,
sharedItemFederationTokens,
signingPrivateKeyPem)
namedAddressesThread = \
sendToNamedAddressesThread(server.session, baseDir,
postToNickname,
domain, onionDomain, i2pDomain, port,
httpPrefix,
federationList,
sendThreads,
postLog,
cachedWebfingers,
personCache,
messageJson, debug,
version,
sharedItemsFederatedDomains,
sharedItemFederationTokens,
signingPrivateKeyPem)
followersThreads.append(namedAddressesThread)
return True

View File

@ -2734,17 +2734,17 @@ def _isProfileUpdate(postJsonObject: {}) -> bool:
return False
def sendToNamedAddresses(session, baseDir: str,
nickname: str, domain: str,
onionDomain: str, i2pDomain: str, port: int,
httpPrefix: str, federationList: [],
sendThreads: [], postLog: [],
cachedWebfingers: {}, personCache: {},
postJsonObject: {}, debug: bool,
projectVersion: str,
sharedItemsFederatedDomains: [],
sharedItemFederationTokens: {},
signingPrivateKeyPem: str) -> None:
def _sendToNamedAddresses(session, baseDir: str,
nickname: str, domain: str,
onionDomain: str, i2pDomain: str, port: int,
httpPrefix: str, federationList: [],
sendThreads: [], postLog: [],
cachedWebfingers: {}, personCache: {},
postJsonObject: {}, debug: bool,
projectVersion: str,
sharedItemsFederatedDomains: [],
sharedItemFederationTokens: {},
signingPrivateKeyPem: str) -> None:
"""sends a post to the specific named addresses in to/cc
"""
if not session:
@ -2885,6 +2885,45 @@ def sendToNamedAddresses(session, baseDir: str,
signingPrivateKeyPem, 34436782)
def sendToNamedAddressesThread(session, baseDir: str,
nickname: str, domain: str,
onionDomain: str, i2pDomain: str, port: int,
httpPrefix: str, federationList: [],
sendThreads: [], postLog: [],
cachedWebfingers: {}, personCache: {},
postJsonObject: {}, debug: bool,
projectVersion: str,
sharedItemsFederatedDomains: [],
sharedItemFederationTokens: {},
signingPrivateKeyPem: str):
"""Returns a thread used to send a post to named addresses
"""
sendThread = \
threadWithTrace(target=_sendToNamedAddresses,
args=(session, baseDir,
nickname, domain,
onionDomain, i2pDomain, port,
httpPrefix, federationList,
sendThreads, postLog,
cachedWebfingers, personCache,
postJsonObject, debug,
projectVersion,
sharedItemsFederatedDomains,
sharedItemFederationTokens,
signingPrivateKeyPem), daemon=True)
try:
sendThread.start()
except SocketError as e:
print('WARN: socket error while starting ' +
'thread to send to named addresses. ' + str(e))
return None
except ValueError as e:
print('WARN: error while starting ' +
'thread to send to named addresses. ' + str(e))
return None
return sendThread
def _hasSharedInbox(session, httpPrefix: str, domain: str,
debug: bool, signingPrivateKeyPem: str) -> bool:
"""Returns true if the given domain has a shared inbox

View File

@ -4507,6 +4507,7 @@ def _testFunctions():
'do_POST',
'do_HEAD',
'__run',
'_sendToNamedAddresses',
'globaltrace',
'localtrace',
'kill',

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "عرّف عن نفسك وحدد التاريخ والوقت اللذين ترغب في الإقامة فيهما",
"Members": "أعضاء",
"Join": "انضم",
"Leave": "يترك"
"Leave": "يترك",
"System Monitor": "مراقب النظام"
}

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "Presenteu-vos i especifiqueu la data i lhora en què voleu romandre",
"Members": "Membres",
"Join": "Uneix-te",
"Leave": "Marxa"
"Leave": "Marxa",
"System Monitor": "Monitor del sistema"
}

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "Cyflwynwch eich hun a nodwch y dyddiad a'r amser pan fyddwch yn dymuno aros",
"Members": "Aelodau",
"Join": "Ymunwch",
"Leave": "Gadewch"
"Leave": "Gadewch",
"System Monitor": "Monitor System"
}

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "Stellen Sie sich vor und geben Sie Datum und Uhrzeit Ihres Aufenthalts an",
"Members": "Mitglieder",
"Join": "Verbinden",
"Leave": "Verlassen"
"Leave": "Verlassen",
"System Monitor": "Systemmonitor"
}

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "Introduce yourself and specify the date and time when you wish to stay",
"Members": "Members",
"Join": "Join",
"Leave": "Leave"
"Leave": "Leave",
"System Monitor": "System Monitor"
}

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "Preséntese y especifique la fecha y hora en que desea quedarse",
"Members": "Miembros",
"Join": "Entrar",
"Leave": "Dejar"
"Leave": "Dejar",
"System Monitor": "Monitor del sistema"
}

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "Présentez-vous et précisez la date et l'heure auxquelles vous souhaitez rester",
"Members": "Membres",
"Join": "Rejoindre",
"Leave": "Laisser"
"Leave": "Laisser",
"System Monitor": "Moniteur système"
}

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "Cuir tú féin in aithne agus sonraigh an dáta agus an t-am ar mhaith leat fanacht",
"Members": "Baill",
"Join": "Bí páirteach",
"Leave": "Fág"
"Leave": "Fág",
"System Monitor": "Monatóir Córais"
}

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "अपना परिचय दें और वह तारीख और समय निर्दिष्ट करें जब आप रुकना चाहते हैं",
"Members": "सदस्यों",
"Join": "शामिल हों",
"Leave": "छोड़ना"
"Leave": "छोड़ना",
"System Monitor": "सिस्टम मॉनिटर"
}

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "Presentati e specifica la data e l'ora in cui desideri soggiornare",
"Members": "Membri",
"Join": "Aderire",
"Leave": "Lasciare"
"Leave": "Lasciare",
"System Monitor": "Monitor di sistema"
}

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "自己紹介をし、滞在したい日時を指定してください",
"Members": "メンバー",
"Join": "加入",
"Leave": "離れる"
"Leave": "離れる",
"System Monitor": "システムモニター"
}

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "Xwe bidin nasîn û roj û dema ku hûn dixwazin bimînin bimînin diyar bikin",
"Members": "Endam",
"Join": "Bihevgirêdan",
"Leave": "Terikandin"
"Leave": "Terikandin",
"System Monitor": "System Monitor"
}

View File

@ -483,5 +483,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "Introduce yourself and specify the date and time when you wish to stay",
"Members": "Members",
"Join": "Join",
"Leave": "Leave"
"Leave": "Leave",
"System Monitor": "System Monitor"
}

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "Apresente-se e especifique a data e hora em que deseja ficar",
"Members": "Membros",
"Join": "Juntar",
"Leave": "Sair"
"Leave": "Sair",
"System Monitor": "Monitor de Sistema"
}

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "Представьтесь и укажите дату и время, когда вы хотите остаться",
"Members": "Члены",
"Join": "Присоединиться",
"Leave": "Оставлять"
"Leave": "Оставлять",
"System Monitor": "Системный монитор"
}

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "Jitambulishe na taja tarehe na saa unapotaka kukaa",
"Members": "Wanachama",
"Join": "Jiunge",
"Leave": "Ondoka"
"Leave": "Ondoka",
"System Monitor": "Ufuatiliaji wa Mfumo"
}

View File

@ -487,5 +487,6 @@
"Introduce yourself and specify the date and time when you wish to stay": "自我介绍并指定您希望入住的日期和时间",
"Members": "会员",
"Join": "加入",
"Leave": "离开"
"Leave": "离开",
"System Monitor": "系统监视器"
}

View File

@ -1415,6 +1415,18 @@ def _htmlEditProfileDangerZone(translate: {}) -> str:
return editProfileForm
def _htmlSystemMonitor(nickname: str, translate: {}) -> str:
"""Links to performance graphs
"""
systemMonitorStr = beginEditSection(translate['System Monitor'])
systemMonitorStr += '<p><a href="/users/' + nickname + \
'/performance?graph=get">📊 GET</a></p>'
systemMonitorStr += '<p><a href="/users/' + nickname + \
'/performance?graph=post">📊 POST</a></p>'
systemMonitorStr += endEditSection()
return systemMonitorStr
def _htmlEditProfileSkills(baseDir: str, nickname: str, domain: str,
translate: {}) -> str:
"""skills section of Edit Profile screen
@ -2038,6 +2050,7 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
roleAssignStr = ''
peertubeStr = ''
libretranslateStr = ''
systemMonitorStr = ''
graphicsStr = ''
sharesFederationStr = ''
@ -2063,6 +2076,7 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
mediaInstanceStr,
blogsInstanceStr,
newsInstanceStr)
systemMonitorStr = _htmlSystemMonitor(nickname, translate)
instanceTitle = getConfigParam(baseDir, 'instanceTitle')
editProfileForm = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
@ -2123,6 +2137,9 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
# automatic translations
editProfileForm += libretranslateStr
# system monitor
editProfileForm += systemMonitorStr
# Filtering and blocking section
replyIntervalHours = getReplyIntervalHours(baseDir, nickname, domain,
defaultReplyIntervalHours)

View File

@ -419,8 +419,28 @@ def htmlSearch(cssCache: {}, translate: {},
'name="submitSearch" accesskey="' + submitKey + '">' + \
translate['Submit'] + '</button>\n'
followStr += ' </form>\n'
followStr += ' <p class="hashtagswarm">' + \
htmlHashTagSwarm(baseDir, actor, translate) + '</p>\n'
cachedHashtagSwarmFilename = \
acctDir(baseDir, searchNickname, domain) + '/.hashtagSwarm'
swarmStr = ''
if os.path.isfile(cachedHashtagSwarmFilename):
try:
with open(cachedHashtagSwarmFilename, 'r') as fp:
swarmStr = fp.read()
except BaseException:
print('WARN: Unable to read cached hashtag swarm')
pass
if not swarmStr:
swarmStr = htmlHashTagSwarm(baseDir, actor, translate)
if swarmStr:
try:
with open(cachedHashtagSwarmFilename, 'w+') as fp:
fp.write(swarmStr)
except BaseException:
print('WARN: Unable to save cached hashtag swarm')
pass
followStr += ' <p class="hashtagswarm">' + swarmStr + '</p>\n'
followStr += ' </center>\n'
followStr += ' </div>\n'
followStr += '</div>\n'
@ -777,7 +797,8 @@ def htmlHashtagSearch(cssCache: {},
# previous page link
hashtagSearchForm += \
' <center>\n' + \
' <a href="/tags/' + hashtag + '?page=' + \
' <a href="/users/' + nickname + \
'/tags/' + hashtag + '?page=' + \
str(pageNumber - 1) + \
'"><img loading="lazy" class="pageicon" src="/' + \
'icons/pageup.png" title="' + \
@ -853,7 +874,7 @@ def htmlHashtagSearch(cssCache: {},
# next page link
hashtagSearchForm += \
' <center>\n' + \
' <a href="/tags/' + hashtag + \
' <a href="/users/' + nickname + '/tags/' + hashtag + \
'?page=' + str(pageNumber + 1) + \
'"><img loading="lazy" class="pageicon" src="/icons' + \
'/pagedown.png" title="' + translate['Page down'] + \