From 5a8757d8b0c853668a5d2f8f71b92769dc15467c Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 10 Aug 2019 11:54:52 +0100 Subject: [PATCH] Hashtag search on public posts --- daemon.py | 42 +++++++++++++++++++++++++- epicyon.py | 9 ++++-- webinterface.py | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 4 deletions(-) diff --git a/daemon.py b/daemon.py index 3a04439bd..eaa2e9e1d 100644 --- a/daemon.py +++ b/daemon.py @@ -76,6 +76,7 @@ from webinterface import htmlUnfollowConfirm from webinterface import htmlProfileAfterSearch from webinterface import htmlEditProfile from webinterface import htmlTermsOfService +from webinterface import htmlHashtagSearch from shares import getSharesFeedForPerson from shares import outboxShareUpload from shares import outboxUndoShareUpload @@ -436,6 +437,7 @@ class PubServer(BaseHTTPRequestHandler): '/sharefiles/' not in self.path and \ '/statuses/' not in self.path and \ '/emoji/' not in self.path and \ + '/tags/' not in self.path and \ '/icons/' not in self.path: divertToLoginScreen=True if self.path.startswith('/users/'): @@ -677,6 +679,30 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy=False return + # hashtag search + if self.path.startswith('/tags/'): + pageNumber=1 + if '?page=' in self.path: + pageNumberStr=self.path.split('?page=')[1] + if pageNumberStr.isdigit(): + pageNumber=int(pageNumberStr) + hashtag=self.path.split('/tags/')[1] + if '?page=' in hashtag: + hashtag=hashtag.split('?page=')[0] + hashtagStr= \ + htmlHashtagSearch(self.server.baseDir,hashtag,pageNumber, \ + maxPostsInFeed,self.server.session, \ + self.server.cachedWebfingers, \ + self.server.personCache) + self._set_headers('text/html',cookie) + if hashtagStr: + self.wfile.write(hashtagStr.encode()) + else: + originPathStr=self.path.split('/tags/')[0] + self._redirect_headers(originPathStr+'/search',cookie) + self.server.GETbusy=False + return + # search for a fediverse address from the web interface by selecting search icon if '/users/' in self.path: if self.path.endswith('/search'): @@ -1931,12 +1957,26 @@ class PubServer(BaseHTTPRequestHandler): actorStr=self.path.replace('/searchhandle','') length = int(self.headers['Content-length']) searchParams=self.rfile.read(length).decode('utf-8') + #print('******************searchParams '+searchParams) if 'searchtext=' in searchParams: searchStr=searchParams.split('searchtext=')[1] if '&' in searchStr: searchStr=searchStr.split('&')[0] - searchStr=searchStr.replace('+',' ').replace('%40','@').replace('%3A',':').strip() + searchStr=searchStr.replace('+',' ').replace('%40','@').replace('%3A',':').replace('%23','#').strip() + if searchStr.startswith('#'): + # hashtag search + hashtagStr= \ + htmlHashtagSearch(self.server.baseDir,searchStr[1:],1, \ + maxPostsInFeed,self.server.session, \ + self.server.cachedWebfingers, \ + self.server.personCache) + if hashtagStr: + self._login_headers('text/html') + self.wfile.write(hashtagStr.encode('utf-8')) + self.server.POSTbusy=False + return if '@' in searchStr: + # profile search print('Search: '+searchStr) nickname=getNicknameFromActor(self.path) if not self.server.session: diff --git a/epicyon.py b/epicyon.py index 4ce4ba8b2..ffce90015 100644 --- a/epicyon.py +++ b/epicyon.py @@ -1193,6 +1193,9 @@ if args.testdata: password='boringpassword' print('Generating some test data for user: '+nickname) + if os.path.isdir(baseDir+'/tags'): + shutil.rmtree(baseDir+'/tags') + setConfigParam(baseDir,'registrationsRemaining',str(maxRegistrations)) createPerson(baseDir,'maxboardroom',domain,port,httpPrefix,True,password) @@ -1231,13 +1234,13 @@ if args.testdata: deleteAllPosts(baseDir,nickname,domain,'inbox') deleteAllPosts(baseDir,nickname,domain,'outbox') - createPublicPost(baseDir,nickname,domain,port,httpPrefix,"like, this is totally just a test, man",False,True,False,None,None,useBlurhash) + createPublicPost(baseDir,nickname,domain,port,httpPrefix,"like, this is totally just a #test, man",False,True,False,None,None,useBlurhash) createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Zoiks!!!",False,True,False,None,None,useBlurhash) - createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Hey scoob we need like a hundred more milkshakes",False,True,False,None,None,useBlurhash) + createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Hey scoob we need like a hundred more #milkshakes",False,True,False,None,None,useBlurhash) createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Getting kinda spooky around here",False,True,False,None,None,useBlurhash,'someone') createPublicPost(baseDir,nickname,domain,port,httpPrefix,"And they would have gotten away with it too if it wasn't for those pesky hackers",False,True,False,'img/logo.png','Description of image',useBlurhash) createPublicPost(baseDir,nickname,domain,port,httpPrefix,"man, these centralized sites are, like, the worst!",False,True,False,None,None,useBlurhash) - createPublicPost(baseDir,nickname,domain,port,httpPrefix,"another mystery solved hey",False,True,False,None,None,useBlurhash) + createPublicPost(baseDir,nickname,domain,port,httpPrefix,"another mystery solved #test",False,True,False,None,None,useBlurhash) createPublicPost(baseDir,nickname,domain,port,httpPrefix,"let's go bowling",False,True,False,None,None,useBlurhash) domainFull=domain+':'+str(port) diff --git a/webinterface.py b/webinterface.py index 8d4ed0602..e80c1c7cf 100644 --- a/webinterface.py +++ b/webinterface.py @@ -33,6 +33,85 @@ from content import getMentionsFromHtml from config import getConfigParam from skills import getSkills +def htmlHashtagSearch(baseDir: str,hashtag: str,pageNumber: int,postsPerPage: int, + session,wfRequest: {},personCache: {}) -> str: + """Show a page containing search results for a hashtag + """ + if hashtag.startswith('#'): + hashtag=hashtag[1:] + hashtagIndexFile=baseDir+'/tags/'+hashtag+'.txt' + if not os.path.isfile(hashtagIndexFile): + return None + + # read the index + with open(hashtagIndexFile, "r") as f: + lines = f.readlines() + + with open(baseDir+'/epicyon-profile.css', 'r') as cssFile: + hashtagSearchCSS = cssFile.read() + + startIndex=len(lines)-1-int(pageNumber*postsPerPage) + if startIndex<0: + startIndex=len(lines)-1 + endIndex=startIndex-postsPerPage + if endIndex<0: + endIndex=0 + + hashtagSearchForm=htmlHeader(hashtagSearchCSS) + hashtagSearchForm+='

Results for #'+hashtag+'

' + if startIndex!=len(lines)-1: + # previous page link + hashtagSearchForm+='
Page up
' + index=startIndex + while index>=endIndex: + postId=lines[index].strip('\n') + nickname=getNicknameFromActor(postId) + if not nickname: + index-=1 + continue + domain,port=getDomainFromActor(postId) + if not domain: + index-=1 + continue + postFilename=locatePost(baseDir,nickname,domain,postId) + if not postFilename: + index-=1 + continue + with open(postFilename, 'r') as fp: + postJsonObject=commentjson.load(fp) + if not postJsonObject.get('type'): + index-=1 + continue + if postJsonObject['type']!='Create': + index-=1 + continue + if not postJsonObject.get('object'): + index-=1 + continue + if not isinstance(postJsonObject['object'], dict): + index-=1 + continue + if not postJsonObject['object'].get('to'): + index-=1 + continue + isPublic=False + for recipient in postJsonObject['object']['to']: + if recipient.endswith('#Public'): + isPublic=True + break + if isPublic: + hashtagSearchForm+= \ + individualPostAsHtml(baseDir,session,wfRequest,personCache, \ + nickname,domain,port,postJsonObject, \ + None,True,False,False) + index-=1 + + if endIndex>0: + # next page link + hashtagSearchForm+='
Page up
' + hashtagSearchForm+=htmlFooter() + return hashtagSearchForm + def htmlEditProfile(baseDir: str,path: str,domain: str,port: int) -> str: """Shows the edit profile screen """