diff --git a/content.py b/content.py index 432462ea..d2b36be5 100644 --- a/content.py +++ b/content.py @@ -9,6 +9,37 @@ __status__ = "Production" import os import commentjson +def validHashTag(hashtag: str) -> bool: + """Returns true if the give hashtag contains valid characters + """ + validChars = set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') + if set(hashtag).issubset(validChars): + return True + return False + +def addHashTags(wordStr: str,httpPrefix: str,domain: str,replaceHashTags: {},postHashtags: {}) -> bool: + """Detects hashtags and adds them to the replacements dict + Also updates the hashtags list to be added to the post + """ + if not wordStr.startswith('#'): + return False + if len(wordStr)<2: + return False + if replaceHashTags.get(wordStr): + return True + hashtag=wordStr[1:] + if not validHashTag(hashtag): + return False + hashtagUrl=httpPrefix+"://"+domain+"/tags/"+hashtag + postHashtags[hashtag]= { + 'href': hashtagUrl, + 'name': '#'+hashtag, + 'type': 'Hashtag' + } + replaceHashTags[wordStr]= \ + "#"+hashtag+"" + return True + def addMention(wordStr: str,httpPrefix: str,following: str,replaceMentions: {},recipients: []) -> bool: """Detects mentions and adds them to the replacements dict and recipients list """ @@ -44,7 +75,7 @@ def addMention(wordStr: str,httpPrefix: str,following: str,replaceMentions: {},r def addHtmlTags(baseDir: str,httpPrefix: str, \ nickname: str,domain: str,content: str, \ - recipients: []) -> str: + recipients: [],hashtags: {}) -> str: """ Replaces plaintext mentions such as @nick@domain into html by matching against known following accounts """ @@ -53,6 +84,7 @@ def addHtmlTags(baseDir: str,httpPrefix: str, \ wordsOnly=content.replace(',',' ').replace(';',' ').replace('.',' ').replace(':',' ') words=wordsOnly.split(' ') replaceMentions={} + replaceHashTags={} if ':' in domain: domain=domain.split(':')[0] followingFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/following.txt' @@ -70,10 +102,14 @@ def addHtmlTags(baseDir: str,httpPrefix: str, \ for wordStr in words: if addMention(wordStr,httpPrefix,following,replaceMentions,recipients): continue + addHashTags(wordStr,httpPrefix,domain,replaceHashTags,hashtags) # replace words with their html versions for wordStr,replaceStr in replaceMentions.items(): content=content.replace(wordStr,replaceStr) + for wordStr,replaceStr in replaceHashTags.items(): + content=content.replace(wordStr,replaceStr) + content=content.replace('\n','

') return '

'+content+'

' diff --git a/daemon.py b/daemon.py index 8552bb9c..c4837af1 100644 --- a/daemon.py +++ b/daemon.py @@ -1838,7 +1838,7 @@ class PubServer(BaseHTTPRequestHandler): addHtmlTags(self.server.baseDir, \ self.server.httpPrefix, \ nickname, \ - self.server.domain,fields['bio'],[]) + self.server.domain,fields['bio'],[],{}) actorChanged=True approveFollowers=False if fields.get('approveFollowers'): diff --git a/posts.py b/posts.py index 8f6aa2e9..c55b53a1 100644 --- a/posts.py +++ b/posts.py @@ -371,6 +371,22 @@ def savePostToBox(baseDir: str,httpPrefix: str,postId: str, \ commentjson.dump(postJsonObject, fp, indent=4, sort_keys=False) return filename +def updateHashtagsIndex(baseDir: str,tag: {},newPostId: str) -> None: + """Writes the post url for hashtags to a file + This allows posts for a hashtag to be quickly looked up + """ + # create hashtags directory + tagsDir=baseDir+'/tags' + if not os.path.isdir(tagsDir): + os.mkdir(tagsDir) + tagName=tag['name'] + tagsFilename=tagsDir+'/'+tagName[1:]+'.txt' + tagFile=open(tagsFilename, "a+") + if not tagFile: + return + tagFile.write(newPostId+'\n') + tagFile.close() + def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \ toUrl: str, ccUrl: str, httpPrefix: str, content: str, \ followersOnly: bool, saveToFile: bool, clientToServer: bool, @@ -379,11 +395,15 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \ """Creates a message """ mentionedRecipients=[] + tags=[] + hashtagsDict={} if not clientToServer: # convert content to html - content=addHtmlTags(baseDir,httpPrefix, \ - nickname,domain,content, \ - mentionedRecipients) + content= \ + addHtmlTags(baseDir,httpPrefix, \ + nickname,domain,content, \ + mentionedRecipients, \ + hashtagsDict) if port!=80 and port!=443: if ':' not in domain: @@ -407,7 +427,13 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \ # who to send to toRecipients=[toUrl] + mentionedRecipients - + + # create a list of hashtags + if hashtagsDict: + for tagName,tag in hashtagsDict.items(): + tags.append(tag) + updateHashtagsIndex(baseDir,tag,newPostId) + if not clientToServer: actorUrl=httpPrefix+'://'+domain+'/users/'+nickname @@ -449,7 +475,7 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \ 'en': content }, 'attachment': [], - 'tag': [], + 'tag': tags, 'replies': { 'id': 'https://'+domain+'/users/'+nickname+'/statuses/'+statusNumber+'/replies', 'type': 'Collection', @@ -486,7 +512,7 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \ 'en': content }, 'attachment': [], - 'tag': [], + 'tag': tags, 'replies': { 'id': 'https://'+domain+'/users/'+nickname+'/statuses/'+statusNumber+'/replies', 'type': 'Collection', diff --git a/tests.py b/tests.py index 9a614b32..b145f5f6 100644 --- a/tests.py +++ b/tests.py @@ -353,7 +353,7 @@ def testPostMessageBetweenServers(): sendResult = \ sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, \ 'bob', bobDomain, bobPort, ccUrl, httpPrefix, \ - 'Why is a mouse when it spins?', followersOnly, \ + 'Why is a mouse when it spins? #sillyquestion', followersOnly, \ saveToFile, clientToServer,attachedImageFilename, \ attachedImageDescription,useBlurhash, federationList, \ aliceSendThreads, alicePostLog, aliceCachedWebfingers, \