Store custom emoji

merge-requests/30/head
Bob Mottram 2021-11-01 17:12:17 +00:00
parent 08e7ed1684
commit 4e81096b32
9 changed files with 159 additions and 34 deletions

21
blog.py
View File

@ -164,7 +164,7 @@ def _getBlogReplies(baseDir: str, httpPrefix: str, translate: {},
return '' return ''
def _htmlBlogPostContent(authorized: bool, def _htmlBlogPostContent(debug: bool, session, authorized: bool,
baseDir: str, httpPrefix: str, translate: {}, baseDir: str, httpPrefix: str, translate: {},
nickname: str, domain: str, domainFull: str, nickname: str, domain: str, domainFull: str,
postJsonObject: {}, postJsonObject: {},
@ -255,9 +255,9 @@ def _htmlBlogPostContent(authorized: bool,
contentStr = addEmbeddedElements(translate, jsonContent, contentStr = addEmbeddedElements(translate, jsonContent,
peertubeInstances) peertubeInstances)
if postJsonObject['object'].get('tag'): if postJsonObject['object'].get('tag'):
contentStr = replaceEmojiFromTags(contentStr, contentStr = replaceEmojiFromTags(session, baseDir, contentStr,
postJsonObject['object']['tag'], postJsonObject['object']['tag'],
'content') 'content', debug)
if articleAdded: if articleAdded:
blogStr += '<br>' + contentStr + '</article>\n' blogStr += '<br>' + contentStr + '</article>\n'
else: else:
@ -414,12 +414,13 @@ def _getSnippetFromBlogContent(postJsonObject: {}, systemLanguage: str) -> str:
return content return content
def htmlBlogPost(authorized: bool, def htmlBlogPost(session, authorized: bool,
baseDir: str, httpPrefix: str, translate: {}, baseDir: str, httpPrefix: str, translate: {},
nickname: str, domain: str, domainFull: str, nickname: str, domain: str, domainFull: str,
postJsonObject: {}, postJsonObject: {},
peertubeInstances: [], peertubeInstances: [],
systemLanguage: str, personCache: {}) -> str: systemLanguage: str, personCache: {},
debug: bool) -> str:
"""Returns a html blog post """Returns a html blog post
""" """
blogStr = '' blogStr = ''
@ -438,7 +439,7 @@ def htmlBlogPost(authorized: bool,
title, snippet) title, snippet)
_htmlBlogRemoveCwButton(blogStr, translate) _htmlBlogRemoveCwButton(blogStr, translate)
blogStr += _htmlBlogPostContent(authorized, baseDir, blogStr += _htmlBlogPostContent(debug, session, authorized, baseDir,
httpPrefix, translate, httpPrefix, translate,
nickname, domain, nickname, domain,
domainFull, postJsonObject, domainFull, postJsonObject,
@ -473,7 +474,7 @@ def htmlBlogPage(authorized: bool, session,
nickname: str, domain: str, port: int, nickname: str, domain: str, port: int,
noOfItems: int, pageNumber: int, noOfItems: int, pageNumber: int,
peertubeInstances: [], systemLanguage: str, peertubeInstances: [], systemLanguage: str,
personCache: {}) -> str: personCache: {}, debug: bool) -> str:
"""Returns a html blog page containing posts """Returns a html blog page containing posts
""" """
if ' ' in nickname or '@' in nickname or \ if ' ' in nickname or '@' in nickname or \
@ -530,7 +531,7 @@ def htmlBlogPage(authorized: bool, session,
if item['type'] != 'Create': if item['type'] != 'Create':
continue continue
blogStr += _htmlBlogPostContent(authorized, baseDir, blogStr += _htmlBlogPostContent(debug, session, authorized, baseDir,
httpPrefix, translate, httpPrefix, translate,
nickname, domain, nickname, domain,
domainFull, item, domainFull, item,
@ -696,7 +697,7 @@ def htmlBlogView(authorized: bool,
translate: {}, domain: str, port: int, translate: {}, domain: str, port: int,
noOfItems: int, noOfItems: int,
peertubeInstances: [], systemLanguage: str, peertubeInstances: [], systemLanguage: str,
personCache: {}) -> str: personCache: {}, debug: bool) -> str:
"""Show the blog main page """Show the blog main page
""" """
blogStr = '' blogStr = ''
@ -715,7 +716,7 @@ def htmlBlogView(authorized: bool,
baseDir, httpPrefix, translate, baseDir, httpPrefix, translate,
nickname, domain, port, nickname, domain, port,
noOfItems, 1, peertubeInstances, noOfItems, 1, peertubeInstances,
systemLanguage, personCache) systemLanguage, personCache, debug)
domainFull = getFullDomain(domain, port) domainFull = getFullDomain(domain, port)

View File

@ -16,6 +16,7 @@ from utils import removeDomainPort
from utils import isValidLanguage from utils import isValidLanguage
from utils import getImageExtensions from utils import getImageExtensions
from utils import loadJson from utils import loadJson
from utils import saveJson
from utils import fileLastModified from utils import fileLastModified
from utils import getLinkPrefixes from utils import getLinkPrefixes
from utils import dangerousMarkup from utils import dangerousMarkup
@ -26,6 +27,7 @@ from utils import isfloat
from utils import getCurrencies from utils import getCurrencies
from utils import removeHtml from utils import removeHtml
from petnames import getPetName from petnames import getPetName
from session import downloadImage
def removeHtmlTag(htmlStr: str, tag: str) -> str: def removeHtmlTag(htmlStr: str, tag: str) -> str:
@ -239,7 +241,40 @@ def switchWords(baseDir: str, nickname: str, domain: str, content: str,
return content return content
def replaceEmojiFromTags(content: str, tag: [], messageType: str) -> str: def _saveCustomEmoji(session, baseDir: str, emojiName: str, url: str,
debug: bool) -> None:
"""Saves custom emoji to file
"""
if not session:
return
if '.' not in url:
return
ext = url.split('.')[-1]
if ext != 'png':
print('Custom emoji is wrong format ' + url)
return
emojiName = emojiName.replace(':').strip()
customEmojiDir = baseDir + '/emojicustom'
if not os.path.isdir(customEmojiDir):
os.mkdir(customEmojiDir)
emojiImageFilename = customEmojiDir + '/' + emojiName + '.' + ext
if not downloadImage(session, baseDir, url,
emojiImageFilename, debug, False):
return
emojiJsonFilename = customEmojiDir + '/emoji.json'
emojiJson = {}
if os.path.isfile(emojiJsonFilename):
emojiJson = loadJson(emojiJsonFilename, 0, 1)
if not emojiJson:
emojiJson = {}
if not emojiJson.get(emojiName):
emojiJson[emojiName] = emojiName
saveJson(emojiJson, emojiJsonFilename)
def replaceEmojiFromTags(session, baseDir: str,
content: str, tag: [], messageType: str,
debug: bool) -> str:
"""Uses the tags to replace :emoji: with html image markup """Uses the tags to replace :emoji: with html image markup
""" """
for tagItem in tag: for tagItem in tag:
@ -265,12 +300,14 @@ def replaceEmojiFromTags(content: str, tag: [], messageType: str) -> str:
iconName = iconName.split('.')[0] iconName = iconName.split('.')[0]
# see https://unicode.org/ # see https://unicode.org/
# emoji/charts/full-emoji-list.html # emoji/charts/full-emoji-list.html
replaced = False
if '-' not in iconName: if '-' not in iconName:
# a single code # a single code
try: try:
replaceChar = chr(int("0x" + iconName, 16)) replaceChar = chr(int("0x" + iconName, 16))
content = content.replace(tagItem['name'], content = content.replace(tagItem['name'],
replaceChar) replaceChar)
replaced = True
except BaseException: except BaseException:
print('EX: replaceEmojiFromTags ' + print('EX: replaceEmojiFromTags ' +
'no conversion of ' + 'no conversion of ' +
@ -278,6 +315,11 @@ def replaceEmojiFromTags(content: str, tag: [], messageType: str) -> str:
tagItem['name'] + ' ' + tagItem['name'] + ' ' +
tagItem['icon']['url']) tagItem['icon']['url'])
pass pass
if not replaced:
_saveCustomEmoji(session, baseDir,
tagItem['name'],
tagItem['icon']['url'],
debug)
else: else:
# sequence of codes # sequence of codes
iconCodes = iconName.split('-') iconCodes = iconName.split('-')
@ -286,6 +328,7 @@ def replaceEmojiFromTags(content: str, tag: [], messageType: str) -> str:
try: try:
iconCodeSequence += chr(int("0x" + iconCodeSequence += chr(int("0x" +
icode, 16)) icode, 16))
replaced = True
except BaseException: except BaseException:
iconCodeSequence = '' iconCodeSequence = ''
print('EX: replaceEmojiFromTags ' + print('EX: replaceEmojiFromTags ' +
@ -294,6 +337,11 @@ def replaceEmojiFromTags(content: str, tag: [], messageType: str) -> str:
tagItem['name'] + ' ' + tagItem['name'] + ' ' +
tagItem['icon']['url']) tagItem['icon']['url'])
break break
if not replaced:
_saveCustomEmoji(session, baseDir,
tagItem['name'],
tagItem['icon']['url'],
debug)
if iconCodeSequence: if iconCodeSequence:
content = content.replace(tagItem['name'], content = content.replace(tagItem['name'],
iconCodeSequence) iconCodeSequence)

View File

@ -11379,7 +11379,8 @@ class PubServer(BaseHTTPRequestHandler):
maxPostsInBlogsFeed, pageNumber, maxPostsInBlogsFeed, pageNumber,
self.server.peertubeInstances, self.server.peertubeInstances,
self.server.systemLanguage, self.server.systemLanguage,
self.server.personCache) self.server.personCache,
self.server.debug)
if msg is not None: if msg is not None:
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
@ -12983,7 +12984,8 @@ class PubServer(BaseHTTPRequestHandler):
maxPostsInBlogsFeed, maxPostsInBlogsFeed,
self.server.peertubeInstances, self.server.peertubeInstances,
self.server.systemLanguage, self.server.systemLanguage,
self.server.personCache) self.server.personCache,
self.server.debug)
if msg is not None: if msg is not None:
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
@ -13076,7 +13078,8 @@ class PubServer(BaseHTTPRequestHandler):
if blogFilename and nickname: if blogFilename and nickname:
postJsonObject = loadJson(blogFilename) postJsonObject = loadJson(blogFilename)
if isBlogPost(postJsonObject): if isBlogPost(postJsonObject):
msg = htmlBlogPost(authorized, msg = htmlBlogPost(self.server.session,
authorized,
self.server.baseDir, self.server.baseDir,
self.server.httpPrefix, self.server.httpPrefix,
self.server.translate, self.server.translate,
@ -13085,7 +13088,8 @@ class PubServer(BaseHTTPRequestHandler):
postJsonObject, postJsonObject,
self.server.peertubeInstances, self.server.peertubeInstances,
self.server.systemLanguage, self.server.systemLanguage,
self.server.personCache) self.server.personCache,
self.server.debug)
if msg is not None: if msg is not None:
msg = msg.encode('utf-8') msg = msg.encode('utf-8')
msglen = len(msg) msglen = len(msg)
@ -15538,8 +15542,11 @@ class PubServer(BaseHTTPRequestHandler):
tags.append(tag) tags.append(tag)
# get list of tags # get list of tags
fields['message'] = \ fields['message'] = \
replaceEmojiFromTags(fields['message'], replaceEmojiFromTags(self.server.session,
tags, 'content') self.server.baseDir,
fields['message'],
tags, 'content',
self.server.debug)
postJsonObject['object']['content'] = fields['message'] postJsonObject['object']['content'] = fields['message']
contentMap = postJsonObject['object']['contentMap'] contentMap = postJsonObject['object']['contentMap']

View File

@ -1292,7 +1292,8 @@ def _createPostModReport(baseDir: str,
modFile.write(newPostId + '\n') modFile.write(newPostId + '\n')
def _createPostBase(baseDir: str, nickname: str, domain: str, port: int, def _createPostBase(baseDir: str,
nickname: str, domain: str, port: int,
toUrl: str, ccUrl: str, httpPrefix: str, content: str, toUrl: str, ccUrl: str, httpPrefix: str, content: str,
followersOnly: bool, saveToFile: bool, followersOnly: bool, saveToFile: bool,
clientToServer: bool, commentsEnabled: bool, clientToServer: bool, commentsEnabled: bool,
@ -1345,7 +1346,9 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
tags.append(tag) tags.append(tag)
# get list of tags # get list of tags
if nickname != 'news': if nickname != 'news':
content = replaceEmojiFromTags(content, tags, 'content') content = \
replaceEmojiFromTags(None, baseDir, content, tags, 'content',
False)
# remove replaced emoji # remove replaced emoji
hashtagsDictCopy = hashtagsDict.copy() hashtagsDictCopy = hashtagsDict.copy()
for tagName, tag in hashtagsDictCopy.items(): for tagName, tag in hashtagsDictCopy.items():

View File

@ -389,3 +389,64 @@ def postImage(session, attachImageFilename: str, federationList: [],
if postResult: if postResult:
return postResult.text return postResult.text
return None return None
def downloadImage(session, baseDir: str, url: str,
imageFilename: str, debug: bool,
force: bool = False) -> bool:
"""Downloads an image
"""
if not url:
return None
# try different image types
imageFormats = {
'png': 'png',
'jpg': 'jpeg',
'jpeg': 'jpeg',
'gif': 'gif',
'svg': 'svg+xml',
'webp': 'webp',
'avif': 'avif'
}
sessionHeaders = None
for imFormat, mimeType in imageFormats.items():
if url.endswith('.' + imFormat) or \
'.' + imFormat + '?' in url:
sessionHeaders = {
'Accept': 'image/' + mimeType
}
if not sessionHeaders:
return False
if not os.path.isfile(imageFilename) or force:
try:
if debug:
print('Downloading image url: ' + url)
result = session.get(url,
headers=sessionHeaders,
params=None)
if result.status_code < 200 or \
result.status_code > 202:
if debug:
print('Image download failed with status ' +
str(result.status_code))
# remove partial download
if os.path.isfile(imageFilename):
try:
os.remove(imageFilename)
except BaseException:
print('EX: downloadImage unable to delete ' +
imageFilename)
pass
else:
with open(imageFilename, 'wb') as f:
f.write(result.content)
if debug:
print('Image downloaded from ' + url)
return True
except Exception as e:
print('EX: Failed to download image: ' +
str(url) + ' ' + str(e))
return False

View File

@ -3379,7 +3379,8 @@ def _testAddEmoji(baseDir: str):
for tagName, tag in hashtags.items(): for tagName, tag in hashtags.items():
tags.append(tag) tags.append(tag)
content = contentModified content = contentModified
contentModified = replaceEmojiFromTags(content, tags, 'content') contentModified = \
replaceEmojiFromTags(None, baseDir, content, tags, 'content', True)
# print('contentModified: ' + contentModified) # print('contentModified: ' + contentModified)
assert contentModified == '<p>Emoji 🍋 🍓 🍌</p>' assert contentModified == '<p>Emoji 🍋 🍓 🍌</p>'

View File

@ -872,7 +872,7 @@ def _getPostTitleAnnounceHtml(baseDir: str,
# add any emoji to the display name # add any emoji to the display name
if ':' in announceDisplayName: if ':' in announceDisplayName:
announceDisplayName = \ announceDisplayName = \
addEmojiToDisplayName(baseDir, httpPrefix, nickname, domain, addEmojiToDisplayName(None, baseDir, httpPrefix, nickname, domain,
announceDisplayName, False) announceDisplayName, False)
_logPostTiming(enableTimingLog, postStartTime, '13.3.1') _logPostTiming(enableTimingLog, postStartTime, '13.3.1')
titleStr += \ titleStr += \
@ -1054,7 +1054,7 @@ def _getPostTitleReplyHtml(baseDir: str,
_logPostTiming(enableTimingLog, postStartTime, '13.5') _logPostTiming(enableTimingLog, postStartTime, '13.5')
replyDisplayName = \ replyDisplayName = \
addEmojiToDisplayName(baseDir, httpPrefix, nickname, domain, addEmojiToDisplayName(None, baseDir, httpPrefix, nickname, domain,
replyDisplayName, False) replyDisplayName, False)
_logPostTiming(enableTimingLog, postStartTime, '13.6') _logPostTiming(enableTimingLog, postStartTime, '13.6')
@ -1316,7 +1316,7 @@ def individualPostAsHtml(signingPrivateKeyPem: str,
# add any emoji to the display name # add any emoji to the display name
if ':' in displayName: if ':' in displayName:
displayName = \ displayName = \
addEmojiToDisplayName(baseDir, httpPrefix, addEmojiToDisplayName(session, baseDir, httpPrefix,
nickname, domain, nickname, domain,
displayName, False) displayName, False)
@ -1442,7 +1442,7 @@ def individualPostAsHtml(signingPrivateKeyPem: str,
if displayName: if displayName:
if ':' in displayName: if ':' in displayName:
displayName = \ displayName = \
addEmojiToDisplayName(baseDir, httpPrefix, addEmojiToDisplayName(session, baseDir, httpPrefix,
nickname, domain, nickname, domain,
displayName, False) displayName, False)
titleStr += \ titleStr += \
@ -1740,7 +1740,7 @@ def individualPostAsHtml(signingPrivateKeyPem: str,
if postJsonObject['object'].get('summary'): if postJsonObject['object'].get('summary'):
cwStr = str(postJsonObject['object']['summary']) cwStr = str(postJsonObject['object']['summary'])
cwStr = \ cwStr = \
addEmojiToDisplayName(baseDir, httpPrefix, addEmojiToDisplayName(session, baseDir, httpPrefix,
nickname, domain, nickname, domain,
cwStr, False) cwStr, False)
contentStr += \ contentStr += \
@ -1768,9 +1768,9 @@ def individualPostAsHtml(signingPrivateKeyPem: str,
if postJsonObject['object'].get('tag') and not isPatch: if postJsonObject['object'].get('tag') and not isPatch:
contentStr = \ contentStr = \
replaceEmojiFromTags(contentStr, replaceEmojiFromTags(session, baseDir, contentStr,
postJsonObject['object']['tag'], postJsonObject['object']['tag'],
'content') 'content', False)
if isMuted: if isMuted:
contentStr = '' contentStr = ''

View File

@ -570,12 +570,12 @@ def htmlProfile(signingPrivateKeyPem: str,
if not domain: if not domain:
return "" return ""
displayName = \ displayName = \
addEmojiToDisplayName(baseDir, httpPrefix, addEmojiToDisplayName(session, baseDir, httpPrefix,
nickname, domain, nickname, domain,
profileJson['name'], True) profileJson['name'], True)
domainFull = getFullDomain(domain, port) domainFull = getFullDomain(domain, port)
profileDescription = \ profileDescription = \
addEmojiToDisplayName(baseDir, httpPrefix, addEmojiToDisplayName(session, baseDir, httpPrefix,
nickname, domain, nickname, domain,
profileJson['summary'], False) profileJson['summary'], False)
postsButton = 'button' postsButton = 'button'
@ -2271,7 +2271,7 @@ def _individualFollowAsHtml(signingPrivateKeyPem: str,
avatarUrl = avatarUrl2 avatarUrl = avatarUrl2
if displayName: if displayName:
displayName = \ displayName = \
addEmojiToDisplayName(baseDir, httpPrefix, addEmojiToDisplayName(None, baseDir, httpPrefix,
actorNickname, domain, actorNickname, domain,
displayName, False) displayName, False)
titleStr = displayName titleStr = displayName

View File

@ -294,7 +294,7 @@ def updateAvatarImageCache(signingPrivateKeyPem: str,
print('avatar image downloaded for ' + actor) print('avatar image downloaded for ' + actor)
return avatarImageFilename.replace(baseDir + '/cache', '') return avatarImageFilename.replace(baseDir + '/cache', '')
except Exception as e: except Exception as e:
print('WARN: Failed to download avatar image: ' + print('EX: Failed to download avatar image: ' +
str(avatarUrl) + ' ' + str(e)) str(avatarUrl) + ' ' + str(e))
prof = 'https://www.w3.org/ns/activitystreams' prof = 'https://www.w3.org/ns/activitystreams'
if '/channel/' not in actor or '/accounts/' not in actor: if '/channel/' not in actor or '/accounts/' not in actor:
@ -781,7 +781,7 @@ def loadIndividualPostAsHtmlFromCache(baseDir: str,
return postHtml return postHtml
def addEmojiToDisplayName(baseDir: str, httpPrefix: str, def addEmojiToDisplayName(session, baseDir: str, httpPrefix: str,
nickname: str, domain: str, nickname: str, domain: str,
displayName: str, inProfileName: bool) -> str: displayName: str, inProfileName: bool) -> str:
"""Adds emoji icons to display names or CW on individual posts """Adds emoji icons to display names or CW on individual posts
@ -804,10 +804,14 @@ def addEmojiToDisplayName(baseDir: str, httpPrefix: str,
# print('TAG: emoji tags list: ' + str(emojiTagsList)) # print('TAG: emoji tags list: ' + str(emojiTagsList))
if not inProfileName: if not inProfileName:
displayName = \ displayName = \
replaceEmojiFromTags(displayName, emojiTagsList, 'post header') replaceEmojiFromTags(session, baseDir,
displayName, emojiTagsList, 'post header',
False)
else: else:
displayName = \ displayName = \
replaceEmojiFromTags(displayName, emojiTagsList, 'profile') replaceEmojiFromTags(session, baseDir,
displayName, emojiTagsList, 'profile',
False)
# print('TAG: displayName after tags 2: ' + displayName) # print('TAG: displayName after tags 2: ' + displayName)
# remove any stray emoji # remove any stray emoji