mirror of https://gitlab.com/bashrc2/epicyon
Implement reply interval in hours
The time after publication of a post during which replies are permittedmain
parent
f84149cd3a
commit
9601220e66
32
daemon.py
32
daemon.py
|
@ -232,6 +232,7 @@ from categories import updateHashtagCategories
|
|||
from languages import getActorLanguages
|
||||
from languages import setActorLanguages
|
||||
from like import updateLikesCollection
|
||||
from utils import canReplyTo
|
||||
from utils import isDM
|
||||
from utils import replaceUsersWithAt
|
||||
from utils import localActorUrl
|
||||
|
@ -857,6 +858,14 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
'This is nothing less ' +
|
||||
'than an utter triumph')
|
||||
|
||||
def _403(self) -> None:
|
||||
if self.server.translate:
|
||||
self._httpReturnCode(403, self.server.translate['Forbidden'],
|
||||
self.server.translate["You're not allowed"])
|
||||
else:
|
||||
self._httpReturnCode(403, 'Forbidden',
|
||||
"You're not allowed")
|
||||
|
||||
def _404(self) -> None:
|
||||
if self.server.translate:
|
||||
self._httpReturnCode(404, self.server.translate['Not Found'],
|
||||
|
@ -11287,6 +11296,18 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
if isNewPostEndpoint:
|
||||
nickname = getNicknameFromActor(path)
|
||||
|
||||
if inReplyToUrl:
|
||||
replyIntervalHours = self.server.defaultReplyIntervalHours
|
||||
if not canReplyTo(baseDir, nickname, domain,
|
||||
inReplyToUrl, replyIntervalHours):
|
||||
print('Reply outside of time window ' + inReplyToUrl)
|
||||
self._403()
|
||||
self.server.GETbusy = False
|
||||
return True
|
||||
elif self.server.debug:
|
||||
print('Reply is within time interval: ' +
|
||||
str(replyIntervalHours) + ' hours')
|
||||
|
||||
accessKeys = self.server.accessKeys
|
||||
if self.server.keyShortcuts.get(nickname):
|
||||
accessKeys = self.server.keyShortcuts[nickname]
|
||||
|
@ -16245,7 +16266,8 @@ def loadTokens(baseDir: str, tokensDict: {}, tokensLookup: {}) -> None:
|
|||
break
|
||||
|
||||
|
||||
def runDaemon(lowBandwidth: bool,
|
||||
def runDaemon(defaultReplyIntervalHours: int,
|
||||
lowBandwidth: bool,
|
||||
maxLikeCount: int,
|
||||
sharedItemsFederatedDomains: [],
|
||||
userAgentsBlocked: [],
|
||||
|
@ -16376,6 +16398,11 @@ def runDaemon(lowBandwidth: bool,
|
|||
'Public': 'p',
|
||||
'Reminder': 'r'
|
||||
}
|
||||
|
||||
# how many hours after a post was publushed can a reply be made
|
||||
defaultReplyIntervalHours = 9999999
|
||||
httpd.defaultReplyIntervalHours = defaultReplyIntervalHours
|
||||
|
||||
httpd.keyShortcuts = {}
|
||||
loadAccessKeysForAccounts(baseDir, httpd.keyShortcuts, httpd.accessKeys)
|
||||
|
||||
|
@ -16704,7 +16731,8 @@ def runDaemon(lowBandwidth: bool,
|
|||
httpd.themeName,
|
||||
httpd.systemLanguage,
|
||||
httpd.maxLikeCount,
|
||||
httpd.signingPrivateKeyPem), daemon=True)
|
||||
httpd.signingPrivateKeyPem,
|
||||
httpd.defaultReplyIntervalHours), daemon=True)
|
||||
|
||||
print('Creating scheduled post thread')
|
||||
httpd.thrPostSchedule = \
|
||||
|
|
|
@ -170,6 +170,11 @@ parser.add_argument('--dormantMonths',
|
|||
default=3,
|
||||
help='How many months does a followed account need to ' +
|
||||
'be unseen for before being considered dormant')
|
||||
parser.add_argument('--defaultReplyIntervalHours',
|
||||
dest='defaultReplyIntervalHours', type=int,
|
||||
default=9999999999,
|
||||
help='How many hours after publication of a post ' +
|
||||
'are replies to it permitted')
|
||||
parser.add_argument('--sendThreadsTimeoutMins',
|
||||
dest='sendThreadsTimeoutMins', type=int,
|
||||
default=30,
|
||||
|
@ -3031,7 +3036,8 @@ if args.defaultCurrency:
|
|||
print('Default currency set to ' + args.defaultCurrency)
|
||||
|
||||
if __name__ == "__main__":
|
||||
runDaemon(args.lowBandwidth, args.maxLikeCount,
|
||||
runDaemon(args.defaultReplyIntervalHours,
|
||||
args.lowBandwidth, args.maxLikeCount,
|
||||
sharedItemsFederatedDomains,
|
||||
userAgentsBlocked,
|
||||
args.logLoginFailures,
|
||||
|
|
39
inbox.py
39
inbox.py
|
@ -15,6 +15,8 @@ import random
|
|||
from linked_data_sig import verifyJsonSignature
|
||||
from languages import understoodPostLanguage
|
||||
from like import updateLikesCollection
|
||||
from utils import getReplyIntervalHours
|
||||
from utils import canReplyTo
|
||||
from utils import getUserPaths
|
||||
from utils import getBaseContentFromPost
|
||||
from utils import acctDir
|
||||
|
@ -2484,7 +2486,8 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int,
|
|||
lastBounceMessage: [],
|
||||
themeName: str, systemLanguage: str,
|
||||
maxLikeCount: int,
|
||||
signingPrivateKeyPem: str) -> bool:
|
||||
signingPrivateKeyPem: str,
|
||||
defaultReplyIntervalHours: int) -> bool:
|
||||
""" Anything which needs to be done after initial checks have passed
|
||||
"""
|
||||
actor = keyId
|
||||
|
@ -2765,11 +2768,29 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int,
|
|||
if isinstance(inReplyTo, str):
|
||||
if not isMuted(baseDir, nickname, domain,
|
||||
inReplyTo, conversationId):
|
||||
actUrl = \
|
||||
localActorUrl(httpPrefix,
|
||||
nickname, domain)
|
||||
_replyNotify(baseDir, handle,
|
||||
actUrl + '/tlreplies')
|
||||
# check if the reply is within the allowed
|
||||
# time period after publication
|
||||
hrs = defaultReplyIntervalHours
|
||||
replyIntervalHours = \
|
||||
getReplyIntervalHours(baseDir,
|
||||
nickname,
|
||||
domain, hrs)
|
||||
if canReplyTo(baseDir, nickname, domain,
|
||||
inReplyTo,
|
||||
replyIntervalHours):
|
||||
actUrl = \
|
||||
localActorUrl(httpPrefix,
|
||||
nickname, domain)
|
||||
_replyNotify(baseDir, handle,
|
||||
actUrl + '/tlreplies')
|
||||
else:
|
||||
if debug:
|
||||
print('Reply to ' + inReplyTo +
|
||||
' is outside of the ' +
|
||||
'permitted interval of ' +
|
||||
str(replyIntervalHours) +
|
||||
' hours')
|
||||
return False
|
||||
else:
|
||||
isReplyToMutedPost = True
|
||||
|
||||
|
@ -3119,7 +3140,8 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int,
|
|||
peertubeInstances: [],
|
||||
verifyAllSignatures: bool,
|
||||
themeName: str, systemLanguage: str,
|
||||
maxLikeCount: int, signingPrivateKeyPem: str) -> None:
|
||||
maxLikeCount: int, signingPrivateKeyPem: str,
|
||||
defaultReplyIntervalHours: int) -> None:
|
||||
"""Processes received items and moves them to the appropriate
|
||||
directories
|
||||
"""
|
||||
|
@ -3534,7 +3556,8 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int,
|
|||
lastBounceMessage,
|
||||
themeName, systemLanguage,
|
||||
maxLikeCount,
|
||||
signingPrivateKeyPem)
|
||||
signingPrivateKeyPem,
|
||||
defaultReplyIntervalHours)
|
||||
if debug:
|
||||
pprint(queueJson['post'])
|
||||
print('Queue: Queue post accepted')
|
||||
|
|
16
tests.py
16
tests.py
|
@ -649,8 +649,10 @@ def createServerAlice(path: str, domain: str, port: int,
|
|||
logLoginFailures = False
|
||||
userAgentsBlocked = []
|
||||
maxLikeCount = 10
|
||||
defaultReplyIntervalHours = 9999999999
|
||||
print('Server running: Alice')
|
||||
runDaemon(lowBandwidth, maxLikeCount,
|
||||
runDaemon(defaultReplyIntervalHours,
|
||||
lowBandwidth, maxLikeCount,
|
||||
sharedItemsFederatedDomains,
|
||||
userAgentsBlocked,
|
||||
logLoginFailures, city,
|
||||
|
@ -785,8 +787,10 @@ def createServerBob(path: str, domain: str, port: int,
|
|||
logLoginFailures = False
|
||||
userAgentsBlocked = []
|
||||
maxLikeCount = 10
|
||||
defaultReplyIntervalHours = 9999999999
|
||||
print('Server running: Bob')
|
||||
runDaemon(lowBandwidth, maxLikeCount,
|
||||
runDaemon(defaultReplyIntervalHours,
|
||||
lowBandwidth, maxLikeCount,
|
||||
sharedItemsFederatedDomains,
|
||||
userAgentsBlocked,
|
||||
logLoginFailures, city,
|
||||
|
@ -850,8 +854,10 @@ def createServerEve(path: str, domain: str, port: int, federationList: [],
|
|||
userAgentsBlocked = []
|
||||
maxLikeCount = 10
|
||||
lowBandwidth = True
|
||||
defaultReplyIntervalHours = 9999999999
|
||||
print('Server running: Eve')
|
||||
runDaemon(lowBandwidth, maxLikeCount,
|
||||
runDaemon(defaultReplyIntervalHours,
|
||||
lowBandwidth, maxLikeCount,
|
||||
sharedItemsFederatedDomains,
|
||||
userAgentsBlocked,
|
||||
logLoginFailures, city,
|
||||
|
@ -917,8 +923,10 @@ def createServerGroup(path: str, domain: str, port: int,
|
|||
userAgentsBlocked = []
|
||||
maxLikeCount = 10
|
||||
lowBandwidth = True
|
||||
defaultReplyIntervalHours = 9999999999
|
||||
print('Server running: Group')
|
||||
runDaemon(lowBandwidth, maxLikeCount,
|
||||
runDaemon(defaultReplyIntervalHours,
|
||||
lowBandwidth, maxLikeCount,
|
||||
sharedItemsFederatedDomains,
|
||||
userAgentsBlocked,
|
||||
logLoginFailures, city,
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "البحث عن العناصر المطلوبة",
|
||||
"Website": "موقع إلكتروني",
|
||||
"Low Bandwidth": "انخفاض النطاق الترددي",
|
||||
"accommodation": "الإقامة"
|
||||
"accommodation": "الإقامة",
|
||||
"Forbidden": "محرم",
|
||||
"You're not allowed": "كنت لا يسمح"
|
||||
}
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "Cerca d'articles desitjats",
|
||||
"Website": "Lloc web",
|
||||
"Low Bandwidth": "Ample de banda baixa",
|
||||
"accommodation": "allotjament"
|
||||
"accommodation": "allotjament",
|
||||
"Forbidden": "Prohibit",
|
||||
"You're not allowed": "No està permès"
|
||||
}
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "Chwilio Eitemau Eisiau",
|
||||
"Website": "Gwefan",
|
||||
"Low Bandwidth": "Lled band isel",
|
||||
"accommodation": "llety"
|
||||
"accommodation": "llety",
|
||||
"Forbidden": "Wedi'i wahardd",
|
||||
"You're not allowed": "Ni chaniateir i chi"
|
||||
}
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "Gesuchte Artikel suchen",
|
||||
"Website": "Webseite",
|
||||
"Low Bandwidth": "Niedrige Bandbreite",
|
||||
"accommodation": "unterkunft"
|
||||
"accommodation": "unterkunft",
|
||||
"Forbidden": "Verboten",
|
||||
"You're not allowed": "Du darfst nicht"
|
||||
}
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "Wanted Items Search",
|
||||
"Website": "Website",
|
||||
"Low Bandwidth": "Low Bandwidth",
|
||||
"accommodation": "accommodation"
|
||||
"accommodation": "accommodation",
|
||||
"Forbidden": "Forbidden",
|
||||
"You're not allowed": "You're not allowed"
|
||||
}
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "Búsqueda de artículos deseados",
|
||||
"Website": "Sitio web",
|
||||
"Low Bandwidth": "Ancho de banda bajo",
|
||||
"accommodation": "alojamiento"
|
||||
"accommodation": "alojamiento",
|
||||
"Forbidden": "Prohibida",
|
||||
"You're not allowed": "No tienes permiso"
|
||||
}
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "Recherche d'objets recherchés",
|
||||
"Website": "Site Internet",
|
||||
"Low Bandwidth": "Bas débit",
|
||||
"accommodation": "hébergement"
|
||||
"accommodation": "hébergement",
|
||||
"Forbidden": "Interdite",
|
||||
"You're not allowed": "Tu n'as pas le droit"
|
||||
}
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "Cuardaigh Míreanna Teastaíonn",
|
||||
"Website": "Suíomh gréasáin",
|
||||
"Low Bandwidth": "Bandaleithead íseal",
|
||||
"accommodation": "lóistín"
|
||||
"accommodation": "lóistín",
|
||||
"Forbidden": "Toirmiscthe",
|
||||
"You're not allowed": "Níl cead agat"
|
||||
}
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "वांटेड आइटम सर्च",
|
||||
"Website": "वेबसाइट",
|
||||
"Low Bandwidth": "कम बैंडविड्थ",
|
||||
"accommodation": "निवास स्थान"
|
||||
"accommodation": "निवास स्थान",
|
||||
"Forbidden": "निषिद्ध",
|
||||
"You're not allowed": "आपको अनुमति नहीं है"
|
||||
}
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "Ricerca articoli ricercati",
|
||||
"Website": "Sito web",
|
||||
"Low Bandwidth": "Bassa larghezza di banda",
|
||||
"accommodation": "struttura ricettiva"
|
||||
"accommodation": "struttura ricettiva",
|
||||
"Forbidden": "Proibita",
|
||||
"You're not allowed": "Non ti è permesso"
|
||||
}
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "欲しいアイテム検索",
|
||||
"Website": "Webサイト",
|
||||
"Low Bandwidth": "低帯域幅",
|
||||
"accommodation": "宿泊施設"
|
||||
"accommodation": "宿泊施設",
|
||||
"Forbidden": "禁断",
|
||||
"You're not allowed": "あなたは許可されていません"
|
||||
}
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "Wanted Items Search",
|
||||
"Website": "Malper",
|
||||
"Low Bandwidth": "Bandwidth kêm",
|
||||
"accommodation": "cih"
|
||||
"accommodation": "cih",
|
||||
"Forbidden": "Qedexekirî",
|
||||
"You're not allowed": "Destûrê nadin te"
|
||||
}
|
||||
|
|
|
@ -471,5 +471,7 @@
|
|||
"Wanted Items Search": "Wanted Items Search",
|
||||
"Website": "Website",
|
||||
"Low Bandwidth": "Low Bandwidth",
|
||||
"accommodation": "accommodation"
|
||||
"accommodation": "accommodation",
|
||||
"Forbidden": "Forbidden",
|
||||
"You're not allowed": "You're not allowed"
|
||||
}
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "Pesquisa de Itens Desejados",
|
||||
"Website": "Local na rede Internet",
|
||||
"Low Bandwidth": "Baixa largura de banda",
|
||||
"accommodation": "alojamento"
|
||||
"accommodation": "alojamento",
|
||||
"Forbidden": "Proibida",
|
||||
"You're not allowed": "Você não tem permissão"
|
||||
}
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "Поиск требуемых предметов",
|
||||
"Website": "Интернет сайт",
|
||||
"Low Bandwidth": "Низкая пропускная способность",
|
||||
"accommodation": "размещение"
|
||||
"accommodation": "размещение",
|
||||
"Forbidden": "Запрещенный",
|
||||
"You're not allowed": "Вам не разрешено"
|
||||
}
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "Utafutaji wa Vitu vinavyotafutwa",
|
||||
"Website": "Tovuti",
|
||||
"Low Bandwidth": "Bandwidth ya chini",
|
||||
"accommodation": "malazi"
|
||||
"accommodation": "malazi",
|
||||
"Forbidden": "Imekatazwa",
|
||||
"You're not allowed": "Hauruhusiwi"
|
||||
}
|
||||
|
|
|
@ -475,5 +475,7 @@
|
|||
"Wanted Items Search": "通缉物品搜索",
|
||||
"Website": "网站",
|
||||
"Low Bandwidth": "低带宽",
|
||||
"accommodation": "住所"
|
||||
"accommodation": "住所",
|
||||
"Forbidden": "禁止的",
|
||||
"You're not allowed": "你不被允许"
|
||||
}
|
||||
|
|
68
utils.py
68
utils.py
|
@ -1317,6 +1317,74 @@ def locatePost(baseDir: str, nickname: str, domain: str,
|
|||
return None
|
||||
|
||||
|
||||
def _getPublishedDate(postJsonObject: {}) -> str:
|
||||
"""Returns the published date on the given post
|
||||
"""
|
||||
published = None
|
||||
if postJsonObject.get('published'):
|
||||
published = postJsonObject['published']
|
||||
elif postJsonObject.get('object'):
|
||||
if isinstance(postJsonObject['object'], dict):
|
||||
if postJsonObject['object'].get('published'):
|
||||
published = postJsonObject['object']['published']
|
||||
if not published:
|
||||
return None
|
||||
if not isinstance(published, str):
|
||||
return None
|
||||
return published
|
||||
|
||||
|
||||
def getReplyIntervalHours(baseDir: str, nickname: str, domain: str,
|
||||
defaultReplyIntervalHours: int) -> int:
|
||||
"""Returns the reply interval for the given account.
|
||||
The reply interval is the number of hours after a post being made
|
||||
during which replies are allowed
|
||||
"""
|
||||
replyIntervalFilename = \
|
||||
acctDir(baseDir, nickname, domain) + '/.replyIntervalHours'
|
||||
if os.path.isfile(replyIntervalFilename):
|
||||
with open(replyIntervalFilename, 'r') as fp:
|
||||
hoursStr = fp.read()
|
||||
if hoursStr.isdigit():
|
||||
return int(hoursStr)
|
||||
return defaultReplyIntervalHours
|
||||
|
||||
|
||||
def canReplyTo(baseDir: str, nickname: str, domain: str,
|
||||
postUrl: str, replyIntervalHours: int,
|
||||
currDateStr: str = None) -> bool:
|
||||
"""Is replying to the given post permitted?
|
||||
This is a spam mitigation feature, so that spammers can't
|
||||
add a lot of replies to old post which you don't notice.
|
||||
"""
|
||||
postFilename = locatePost(baseDir, nickname, domain, postUrl)
|
||||
if not postFilename:
|
||||
return False
|
||||
postJsonObject = loadJson(postFilename)
|
||||
if not postJsonObject:
|
||||
return False
|
||||
published = _getPublishedDate(postJsonObject)
|
||||
if not published:
|
||||
return False
|
||||
try:
|
||||
pubDate = datetime.datetime.strptime(published, '%Y-%m-%dT%H:%M:%SZ')
|
||||
except BaseException:
|
||||
return False
|
||||
if not currDateStr:
|
||||
currDate = datetime.datetime.utcnow()
|
||||
else:
|
||||
try:
|
||||
currDate = datetime.datetime.strptime(currDateStr,
|
||||
'%Y-%m-%dT%H:%M:%SZ')
|
||||
except BaseException:
|
||||
return False
|
||||
hoursSincePublication = int((currDate - pubDate).total_seconds() / 3600)
|
||||
if hoursSincePublication < 0 or \
|
||||
hoursSincePublication > replyIntervalHours:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _removeAttachment(baseDir: str, httpPrefix: str, domain: str,
|
||||
postJson: {}):
|
||||
if not postJson.get('attachment'):
|
||||
|
|
Loading…
Reference in New Issue