epicyon/reaction.py

592 lines
22 KiB
Python
Raw Normal View History

2021-11-10 12:16:03 +00:00
__filename__ = "reaction.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.2.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@libreserver.org"
__status__ = "Production"
__module_group__ = "ActivityPub"
import os
2021-11-10 13:10:02 +00:00
import re
2021-11-10 21:43:48 +00:00
import urllib.parse
2021-11-10 12:16:03 +00:00
from pprint import pprint
from utils import hasObjectString
from utils import hasObjectStringObject
from utils import hasObjectStringType
from utils import removeDomainPort
2021-12-26 10:57:03 +00:00
from utils import has_object_dict
2021-11-10 12:16:03 +00:00
from utils import hasUsersPath
from utils import getFullDomain
from utils import removeIdEnding
from utils import urlPermitted
from utils import getNicknameFromActor
from utils import getDomainFromActor
from utils import locatePost
from utils import undoReactionCollectionEntry
from utils import hasGroupType
2021-12-26 10:19:59 +00:00
from utils import local_actor_url
2021-11-10 12:16:03 +00:00
from utils import loadJson
from utils import saveJson
from utils import removePostFromCache
from utils import getCachedPostFilename
from utils import containsInvalidChars
2021-11-10 12:16:03 +00:00
from posts import sendSignedJson
from session import postJson
from webfinger import webfingerHandle
from auth import createBasicAuthHeader
from posts import getPersonBox
2021-11-10 17:35:54 +00:00
# the maximum number of reactions from individual actors which can be
# added to a post. Hence an adversary can't bombard you with sockpuppet
# generated reactions and make the post infeasibly large
maxActorReactionsPerPost = 64
2021-11-10 12:16:03 +00:00
2021-11-10 17:35:54 +00:00
# regex defining permissable emoji icon range
2021-11-10 13:10:02 +00:00
emojiRegex = re.compile(r'[\u263a-\U0001f645]')
def validEmojiContent(emojiContent: str) -> bool:
"""Is the given emoji content valid?
"""
if not emojiContent:
return False
if len(emojiContent) > 2:
2021-11-10 13:10:02 +00:00
return False
if len(emojiRegex.findall(emojiContent)) == 0:
return False
if containsInvalidChars(emojiContent):
return False
2021-11-10 13:10:02 +00:00
return True
2021-11-10 12:16:03 +00:00
def _reaction(recentPostsCache: {},
2021-12-25 23:45:30 +00:00
session, base_dir: str, federation_list: [],
2021-11-10 12:16:03 +00:00
nickname: str, domain: str, port: int,
2021-12-25 17:09:22 +00:00
ccList: [], http_prefix: str,
2021-11-10 12:16:03 +00:00
objectUrl: str, emojiContent: str,
actorReaction: str,
2021-12-25 20:39:35 +00:00
client_to_server: bool,
2021-12-25 21:37:41 +00:00
send_threads: [], postLog: [],
2021-12-25 22:28:18 +00:00
person_cache: {}, cached_webfingers: {},
2021-12-25 20:34:38 +00:00
debug: bool, project_version: str,
2021-12-25 23:03:28 +00:00
signing_priv_key_pem: str) -> {}:
2021-11-10 12:16:03 +00:00
"""Creates an emoji reaction
actor is the person doing the reacting
'to' might be a specific person (actor) whose post was reaction
object is typically the url of the message which was reaction
"""
2021-12-25 23:45:30 +00:00
if not urlPermitted(objectUrl, federation_list):
2021-11-10 12:16:03 +00:00
return None
2021-11-10 13:10:02 +00:00
if not validEmojiContent(emojiContent):
print('_reaction: Invalid emoji reaction: "' + emojiContent + '"')
return
2021-11-10 12:16:03 +00:00
fullDomain = getFullDomain(domain, port)
newReactionJson = {
"@context": "https://www.w3.org/ns/activitystreams",
'type': 'EmojiReact',
2021-12-26 10:19:59 +00:00
'actor': local_actor_url(http_prefix, nickname, fullDomain),
2021-11-10 12:16:03 +00:00
'object': objectUrl,
'content': emojiContent
}
if ccList:
if len(ccList) > 0:
newReactionJson['cc'] = ccList
# Extract the domain and nickname from a statuses link
reactionPostNickname = None
reactionPostDomain = None
reactionPostPort = None
2021-12-26 00:07:44 +00:00
group_account = False
2021-11-10 12:16:03 +00:00
if actorReaction:
reactionPostNickname = getNicknameFromActor(actorReaction)
reactionPostDomain, reactionPostPort = \
getDomainFromActor(actorReaction)
2021-12-26 00:07:44 +00:00
group_account = hasGroupType(base_dir, actorReaction, person_cache)
2021-11-10 12:16:03 +00:00
else:
if hasUsersPath(objectUrl):
reactionPostNickname = getNicknameFromActor(objectUrl)
reactionPostDomain, reactionPostPort = \
getDomainFromActor(objectUrl)
if '/' + str(reactionPostNickname) + '/' in objectUrl:
actorReaction = \
objectUrl.split('/' + reactionPostNickname + '/')[0] + \
'/' + reactionPostNickname
2021-12-26 00:07:44 +00:00
group_account = \
2021-12-25 22:17:49 +00:00
hasGroupType(base_dir, actorReaction, person_cache)
2021-11-10 12:16:03 +00:00
if reactionPostNickname:
2021-12-25 16:17:53 +00:00
postFilename = locatePost(base_dir, nickname, domain, objectUrl)
2021-11-10 12:16:03 +00:00
if not postFilename:
2021-12-25 16:17:53 +00:00
print('DEBUG: reaction base_dir: ' + base_dir)
2021-11-10 12:16:03 +00:00
print('DEBUG: reaction nickname: ' + nickname)
print('DEBUG: reaction domain: ' + domain)
print('DEBUG: reaction objectUrl: ' + objectUrl)
return None
updateReactionCollection(recentPostsCache,
2021-12-25 16:17:53 +00:00
base_dir, postFilename, objectUrl,
2021-11-10 12:16:03 +00:00
newReactionJson['actor'],
nickname, domain, debug, None,
emojiContent)
2021-12-25 16:17:53 +00:00
sendSignedJson(newReactionJson, session, base_dir,
2021-11-10 12:16:03 +00:00
nickname, domain, port,
reactionPostNickname,
reactionPostDomain, reactionPostPort,
'https://www.w3.org/ns/activitystreams#Public',
2021-12-25 23:45:30 +00:00
http_prefix, True, client_to_server, federation_list,
2021-12-25 22:28:18 +00:00
send_threads, postLog, cached_webfingers, person_cache,
2021-12-26 00:07:44 +00:00
debug, project_version, None, group_account,
2021-12-25 23:03:28 +00:00
signing_priv_key_pem, 7165392)
2021-11-10 12:16:03 +00:00
return newReactionJson
def reactionPost(recentPostsCache: {},
2021-12-25 23:45:30 +00:00
session, base_dir: str, federation_list: [],
2021-12-25 17:09:22 +00:00
nickname: str, domain: str, port: int, http_prefix: str,
2021-11-10 12:16:03 +00:00
reactionNickname: str, reactionDomain: str, reactionPort: int,
ccList: [],
reactionStatusNumber: int, emojiContent: str,
2021-12-25 20:39:35 +00:00
client_to_server: bool,
2021-12-25 21:37:41 +00:00
send_threads: [], postLog: [],
2021-12-25 22:28:18 +00:00
person_cache: {}, cached_webfingers: {},
2021-12-25 20:34:38 +00:00
debug: bool, project_version: str,
2021-12-25 23:03:28 +00:00
signing_priv_key_pem: str) -> {}:
2021-11-10 12:16:03 +00:00
"""Adds a reaction to a given status post. This is only used by unit tests
"""
reactionDomain = getFullDomain(reactionDomain, reactionPort)
2021-12-25 17:09:22 +00:00
actorReaction = \
2021-12-26 10:19:59 +00:00
local_actor_url(http_prefix, reactionNickname, reactionDomain)
2021-11-10 12:16:03 +00:00
objectUrl = actorReaction + '/statuses/' + str(reactionStatusNumber)
return _reaction(recentPostsCache,
2021-12-25 23:45:30 +00:00
session, base_dir, federation_list,
2021-11-10 12:16:03 +00:00
nickname, domain, port,
2021-12-25 17:09:22 +00:00
ccList, http_prefix, objectUrl, emojiContent,
2021-12-25 20:39:35 +00:00
actorReaction, client_to_server,
2021-12-25 22:28:18 +00:00
send_threads, postLog, person_cache, cached_webfingers,
2021-12-25 23:03:28 +00:00
debug, project_version, signing_priv_key_pem)
2021-11-10 12:16:03 +00:00
2021-12-25 16:17:53 +00:00
def sendReactionViaServer(base_dir: str, session,
2021-11-10 12:16:03 +00:00
fromNickname: str, password: str,
fromDomain: str, fromPort: int,
2021-12-25 17:09:22 +00:00
http_prefix: str, reactionUrl: str,
2021-11-10 12:16:03 +00:00
emojiContent: str,
2021-12-25 22:28:18 +00:00
cached_webfingers: {}, person_cache: {},
2021-12-25 20:34:38 +00:00
debug: bool, project_version: str,
2021-12-25 23:03:28 +00:00
signing_priv_key_pem: str) -> {}:
2021-11-10 12:16:03 +00:00
"""Creates a reaction via c2s
"""
if not session:
print('WARN: No session for sendReactionViaServer')
return 6
2021-11-10 13:10:02 +00:00
if not validEmojiContent(emojiContent):
print('sendReactionViaServer: Invalid emoji reaction: "' +
emojiContent + '"')
return 7
2021-11-10 12:16:03 +00:00
fromDomainFull = getFullDomain(fromDomain, fromPort)
2021-12-26 10:19:59 +00:00
actor = local_actor_url(http_prefix, fromNickname, fromDomainFull)
2021-11-10 12:16:03 +00:00
newReactionJson = {
"@context": "https://www.w3.org/ns/activitystreams",
'type': 'EmojiReact',
'actor': actor,
'object': reactionUrl,
'content': emojiContent
}
2021-12-25 17:09:22 +00:00
handle = http_prefix + '://' + fromDomainFull + '/@' + fromNickname
2021-11-10 12:16:03 +00:00
# lookup the inbox for the To handle
2021-12-25 17:09:22 +00:00
wfRequest = webfingerHandle(session, handle, http_prefix,
2021-12-25 22:28:18 +00:00
cached_webfingers,
2021-12-25 20:34:38 +00:00
fromDomain, project_version, debug, False,
2021-12-25 23:03:28 +00:00
signing_priv_key_pem)
2021-11-10 12:16:03 +00:00
if not wfRequest:
if debug:
print('DEBUG: reaction webfinger failed for ' + handle)
return 1
if not isinstance(wfRequest, dict):
print('WARN: reaction webfinger for ' + handle +
' did not return a dict. ' + str(wfRequest))
return 1
postToBox = 'outbox'
# get the actor inbox for the To handle
originDomain = fromDomain
(inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, avatarUrl,
2021-12-25 23:03:28 +00:00
displayName, _) = getPersonBox(signing_priv_key_pem,
2021-11-10 12:16:03 +00:00
originDomain,
2021-12-25 16:17:53 +00:00
base_dir, session, wfRequest,
2021-12-25 22:17:49 +00:00
person_cache,
2021-12-25 20:34:38 +00:00
project_version, http_prefix,
2021-11-10 12:16:03 +00:00
fromNickname, fromDomain,
postToBox, 72873)
if not inboxUrl:
if debug:
print('DEBUG: reaction no ' + postToBox +
' was found for ' + handle)
return 3
if not fromPersonId:
if debug:
print('DEBUG: reaction no actor was found for ' + handle)
return 4
authHeader = createBasicAuthHeader(fromNickname, password)
headers = {
'host': fromDomain,
'Content-type': 'application/json',
'Authorization': authHeader
}
2021-12-25 17:09:22 +00:00
postResult = postJson(http_prefix, fromDomainFull,
2021-11-10 12:16:03 +00:00
session, newReactionJson, [], inboxUrl,
headers, 3, True)
if not postResult:
if debug:
print('WARN: POST reaction failed for c2s to ' + inboxUrl)
return 5
if debug:
print('DEBUG: c2s POST reaction success')
return newReactionJson
2021-12-25 16:17:53 +00:00
def sendUndoReactionViaServer(base_dir: str, session,
2021-11-10 12:16:03 +00:00
fromNickname: str, password: str,
fromDomain: str, fromPort: int,
2021-12-25 17:09:22 +00:00
http_prefix: str, reactionUrl: str,
2021-11-10 12:16:03 +00:00
emojiContent: str,
2021-12-25 22:28:18 +00:00
cached_webfingers: {}, person_cache: {},
2021-12-25 20:34:38 +00:00
debug: bool, project_version: str,
2021-12-25 23:03:28 +00:00
signing_priv_key_pem: str) -> {}:
2021-11-10 12:16:03 +00:00
"""Undo a reaction via c2s
"""
if not session:
print('WARN: No session for sendUndoReactionViaServer')
return 6
fromDomainFull = getFullDomain(fromDomain, fromPort)
2021-12-26 10:19:59 +00:00
actor = local_actor_url(http_prefix, fromNickname, fromDomainFull)
2021-11-10 12:16:03 +00:00
newUndoReactionJson = {
"@context": "https://www.w3.org/ns/activitystreams",
'type': 'Undo',
'actor': actor,
'object': {
'type': 'EmojiReact',
'actor': actor,
'object': reactionUrl,
'content': emojiContent
}
}
2021-12-25 17:09:22 +00:00
handle = http_prefix + '://' + fromDomainFull + '/@' + fromNickname
2021-11-10 12:16:03 +00:00
# lookup the inbox for the To handle
2021-12-25 17:09:22 +00:00
wfRequest = webfingerHandle(session, handle, http_prefix,
2021-12-25 22:28:18 +00:00
cached_webfingers,
2021-12-25 20:34:38 +00:00
fromDomain, project_version, debug, False,
2021-12-25 23:03:28 +00:00
signing_priv_key_pem)
2021-11-10 12:16:03 +00:00
if not wfRequest:
if debug:
print('DEBUG: unreaction webfinger failed for ' + handle)
return 1
if not isinstance(wfRequest, dict):
if debug:
print('WARN: unreaction webfinger for ' + handle +
' did not return a dict. ' + str(wfRequest))
return 1
postToBox = 'outbox'
# get the actor inbox for the To handle
originDomain = fromDomain
(inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, avatarUrl,
2021-12-25 23:03:28 +00:00
displayName, _) = getPersonBox(signing_priv_key_pem,
2021-11-10 12:16:03 +00:00
originDomain,
2021-12-25 16:17:53 +00:00
base_dir, session, wfRequest,
2021-12-25 22:17:49 +00:00
person_cache, project_version,
2021-12-25 17:09:22 +00:00
http_prefix, fromNickname,
2021-11-10 12:16:03 +00:00
fromDomain, postToBox,
72625)
if not inboxUrl:
if debug:
print('DEBUG: unreaction no ' + postToBox +
' was found for ' + handle)
return 3
if not fromPersonId:
if debug:
print('DEBUG: unreaction no actor was found for ' + handle)
return 4
authHeader = createBasicAuthHeader(fromNickname, password)
headers = {
'host': fromDomain,
'Content-type': 'application/json',
'Authorization': authHeader
}
2021-12-25 17:09:22 +00:00
postResult = postJson(http_prefix, fromDomainFull,
2021-11-10 12:16:03 +00:00
session, newUndoReactionJson, [], inboxUrl,
headers, 3, True)
if not postResult:
if debug:
print('WARN: POST unreaction failed for c2s to ' + inboxUrl)
return 5
if debug:
print('DEBUG: c2s POST unreaction success')
return newUndoReactionJson
def outboxReaction(recentPostsCache: {},
2021-12-25 17:09:22 +00:00
base_dir: str, http_prefix: str,
2021-11-10 12:16:03 +00:00
nickname: str, domain: str, port: int,
2021-12-25 23:51:19 +00:00
message_json: {}, debug: bool) -> None:
2021-11-10 12:16:03 +00:00
""" When a reaction request is received by the outbox from c2s
"""
2021-12-25 23:51:19 +00:00
if not message_json.get('type'):
2021-11-10 12:16:03 +00:00
if debug:
print('DEBUG: reaction - no type')
return
2021-12-25 23:51:19 +00:00
if not message_json['type'] == 'EmojiReact':
2021-11-10 12:16:03 +00:00
if debug:
print('DEBUG: not a reaction')
return
2021-12-25 23:51:19 +00:00
if not hasObjectString(message_json, debug):
2021-11-10 12:16:03 +00:00
return
2021-12-25 23:51:19 +00:00
if not message_json.get('content'):
2021-11-10 12:16:03 +00:00
return
2021-12-25 23:51:19 +00:00
if not isinstance(message_json['content'], str):
2021-11-10 12:16:03 +00:00
return
2021-12-25 23:51:19 +00:00
if not validEmojiContent(message_json['content']):
2021-11-10 13:10:02 +00:00
print('outboxReaction: Invalid emoji reaction: "' +
2021-12-25 23:51:19 +00:00
message_json['content'] + '"')
2021-11-10 13:10:02 +00:00
return
2021-11-10 12:16:03 +00:00
if debug:
print('DEBUG: c2s reaction request arrived in outbox')
2021-12-25 23:51:19 +00:00
messageId = removeIdEnding(message_json['object'])
2021-11-10 12:16:03 +00:00
domain = removeDomainPort(domain)
2021-12-25 23:51:19 +00:00
emojiContent = message_json['content']
2021-12-25 16:17:53 +00:00
postFilename = locatePost(base_dir, nickname, domain, messageId)
2021-11-10 12:16:03 +00:00
if not postFilename:
if debug:
print('DEBUG: c2s reaction post not found in inbox or outbox')
print(messageId)
return True
updateReactionCollection(recentPostsCache,
2021-12-25 16:17:53 +00:00
base_dir, postFilename, messageId,
2021-12-25 23:51:19 +00:00
message_json['actor'],
2021-11-10 12:16:03 +00:00
nickname, domain, debug, None, emojiContent)
if debug:
print('DEBUG: post reaction via c2s - ' + postFilename)
def outboxUndoReaction(recentPostsCache: {},
2021-12-25 17:09:22 +00:00
base_dir: str, http_prefix: str,
2021-11-10 12:16:03 +00:00
nickname: str, domain: str, port: int,
2021-12-25 23:51:19 +00:00
message_json: {}, debug: bool) -> None:
2021-11-10 12:16:03 +00:00
""" When an undo reaction request is received by the outbox from c2s
"""
2021-12-25 23:51:19 +00:00
if not message_json.get('type'):
2021-11-10 12:16:03 +00:00
return
2021-12-25 23:51:19 +00:00
if not message_json['type'] == 'Undo':
2021-11-10 12:16:03 +00:00
return
2021-12-25 23:51:19 +00:00
if not hasObjectStringType(message_json, debug):
2021-11-10 12:16:03 +00:00
return
2021-12-25 23:51:19 +00:00
if not message_json['object']['type'] == 'EmojiReact':
2021-11-10 12:16:03 +00:00
if debug:
print('DEBUG: not a undo reaction')
return
2021-12-25 23:51:19 +00:00
if not message_json['object'].get('content'):
2021-11-10 12:16:03 +00:00
return
2021-12-25 23:51:19 +00:00
if not isinstance(message_json['object']['content'], str):
2021-11-10 12:16:03 +00:00
return
2021-12-25 23:51:19 +00:00
if not hasObjectStringObject(message_json, debug):
2021-11-10 12:16:03 +00:00
return
if debug:
print('DEBUG: c2s undo reaction request arrived in outbox')
2021-12-25 23:51:19 +00:00
messageId = removeIdEnding(message_json['object']['object'])
emojiContent = message_json['object']['content']
2021-11-10 12:16:03 +00:00
domain = removeDomainPort(domain)
2021-12-25 16:17:53 +00:00
postFilename = locatePost(base_dir, nickname, domain, messageId)
2021-11-10 12:16:03 +00:00
if not postFilename:
if debug:
print('DEBUG: c2s undo reaction post not found in inbox or outbox')
print(messageId)
return True
2021-12-25 16:17:53 +00:00
undoReactionCollectionEntry(recentPostsCache, base_dir, postFilename,
2021-12-25 23:51:19 +00:00
messageId, message_json['actor'],
2021-11-10 12:16:03 +00:00
domain, debug, None, emojiContent)
if debug:
print('DEBUG: post undo reaction via c2s - ' + postFilename)
def updateReactionCollection(recentPostsCache: {},
2021-12-25 16:17:53 +00:00
base_dir: str, postFilename: str,
2021-11-10 12:16:03 +00:00
objectUrl: str, actor: str,
nickname: str, domain: str, debug: bool,
2021-12-25 22:09:19 +00:00
post_json_object: {},
2021-11-10 12:16:03 +00:00
emojiContent: str) -> None:
"""Updates the reactions collection within a post
"""
2021-12-25 22:09:19 +00:00
if not post_json_object:
post_json_object = loadJson(postFilename)
if not post_json_object:
2021-11-10 12:16:03 +00:00
return
# remove any cached version of this post so that the
# reaction icon is changed
2021-12-25 22:09:19 +00:00
removePostFromCache(post_json_object, recentPostsCache)
2021-12-25 16:17:53 +00:00
cachedPostFilename = getCachedPostFilename(base_dir, nickname,
2021-12-25 22:09:19 +00:00
domain, post_json_object)
2021-11-10 12:16:03 +00:00
if cachedPostFilename:
if os.path.isfile(cachedPostFilename):
try:
os.remove(cachedPostFilename)
2021-11-25 18:42:38 +00:00
except OSError:
2021-11-10 12:16:03 +00:00
print('EX: updateReactionCollection unable to delete ' +
cachedPostFilename)
2021-12-25 22:09:19 +00:00
obj = post_json_object
2021-12-26 10:57:03 +00:00
if has_object_dict(post_json_object):
2021-12-25 22:09:19 +00:00
obj = post_json_object['object']
2021-11-10 12:16:03 +00:00
if not objectUrl.endswith('/reactions'):
objectUrl = objectUrl + '/reactions'
if not obj.get('reactions'):
if debug:
print('DEBUG: Adding initial emoji reaction to ' + objectUrl)
reactionsJson = {
"@context": "https://www.w3.org/ns/activitystreams",
'id': objectUrl,
'type': 'Collection',
"totalItems": 1,
'items': [{
'type': 'EmojiReact',
'actor': actor,
'content': emojiContent
}]
}
obj['reactions'] = reactionsJson
else:
if not obj['reactions'].get('items'):
obj['reactions']['items'] = []
2021-11-10 17:35:54 +00:00
# upper limit for the number of reactions on a post
if len(obj['reactions']['items']) >= maxActorReactionsPerPost:
return
2021-11-10 12:16:03 +00:00
for reactionItem in obj['reactions']['items']:
if reactionItem.get('actor') and reactionItem.get('content'):
if reactionItem['actor'] == actor and \
reactionItem['content'] == emojiContent:
# already reaction
return
newReaction = {
'type': 'EmojiReact',
'actor': actor,
'content': emojiContent
}
obj['reactions']['items'].append(newReaction)
itlen = len(obj['reactions']['items'])
obj['reactions']['totalItems'] = itlen
if debug:
print('DEBUG: saving post with emoji reaction added')
2021-12-25 22:09:19 +00:00
pprint(post_json_object)
saveJson(post_json_object, postFilename)
2021-11-10 17:14:51 +00:00
2021-12-25 22:09:19 +00:00
def htmlEmojiReactions(post_json_object: {}, interactive: bool,
2021-11-12 11:40:27 +00:00
actor: str, maxReactionTypes: int,
boxName: str, pageNumber: int) -> str:
2021-11-10 17:14:51 +00:00
"""html containing row of emoji reactions
displayed at the bottom of posts, above the icons
2021-11-10 17:14:51 +00:00
"""
2021-12-26 10:57:03 +00:00
if not has_object_dict(post_json_object):
2021-11-10 17:14:51 +00:00
return ''
2021-12-25 22:09:19 +00:00
if not post_json_object.get('actor'):
2021-11-12 11:40:27 +00:00
return ''
2021-12-25 22:09:19 +00:00
if not post_json_object['object'].get('reactions'):
2021-11-10 17:14:51 +00:00
return ''
2021-12-25 22:09:19 +00:00
if not post_json_object['object']['reactions'].get('items'):
2021-11-10 17:14:51 +00:00
return ''
reactions = {}
2021-11-10 21:55:56 +00:00
reactedToByThisActor = []
2021-12-25 22:09:19 +00:00
for item in post_json_object['object']['reactions']['items']:
2021-11-10 17:14:51 +00:00
emojiContent = item['content']
2021-11-10 21:55:56 +00:00
emojiActor = item['actor']
emojiNickname = getNicknameFromActor(emojiActor)
emojiDomain, _ = getDomainFromActor(emojiActor)
emojiHandle = emojiNickname + '@' + emojiDomain
2021-11-10 21:55:56 +00:00
if emojiActor == actor:
if emojiContent not in reactedToByThisActor:
reactedToByThisActor.append(emojiContent)
2021-11-10 17:14:51 +00:00
if not reactions.get(emojiContent):
if len(reactions.items()) < maxReactionTypes:
reactions[emojiContent] = {
"handles": [emojiHandle],
"count": 1
}
2021-11-10 17:14:51 +00:00
else:
reactions[emojiContent]['count'] += 1
if len(reactions[emojiContent]['handles']) < 32:
reactions[emojiContent]['handles'].append(emojiHandle)
2021-11-10 17:14:51 +00:00
if len(reactions.items()) == 0:
return ''
2021-12-25 22:09:19 +00:00
reactBy = removeIdEnding(post_json_object['object']['id'])
2021-11-10 17:14:51 +00:00
htmlStr = '<div class="emojiReactionBar">\n'
for emojiContent, item in reactions.items():
count = item['count']
# get the handles of actors who reacted
handlesStr = ''
item['handles'].sort()
for handle in item['handles']:
if handlesStr:
handlesStr += '&#10;'
handlesStr += handle
2021-11-10 21:55:56 +00:00
if emojiContent not in reactedToByThisActor:
2021-11-12 11:40:27 +00:00
baseUrl = actor + '?react=' + reactBy
2021-11-10 21:55:56 +00:00
else:
2021-11-12 11:40:27 +00:00
baseUrl = actor + '?unreact=' + reactBy
2021-12-25 22:09:19 +00:00
baseUrl += '?actor=' + post_json_object['actor']
2021-11-12 11:40:27 +00:00
baseUrl += '?tl=' + boxName
baseUrl += '?page=' + str(pageNumber)
baseUrl += '?emojreact='
2021-11-10 21:55:56 +00:00
2021-11-10 17:14:51 +00:00
htmlStr += ' <div class="emojiReactionButton">\n'
if count < 100:
countStr = str(count)
else:
countStr = '99+'
emojiContentStr = emojiContent + countStr
if interactive:
# urlencode the emoji
2021-11-10 21:43:48 +00:00
emojiContentEncoded = urllib.parse.quote_plus(emojiContent)
2021-11-10 17:14:51 +00:00
emojiContentStr = \
' <a href="' + baseUrl + emojiContentEncoded + \
'" title="' + handlesStr + '">' + \
2021-11-10 17:14:51 +00:00
emojiContentStr + '</a>\n'
htmlStr += emojiContentStr
htmlStr += ' </div>\n'
htmlStr += '</div>\n'
return htmlStr