like.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. __filename__ = "like.py"
  2. __author__ = "Bob Mottram"
  3. __license__ = "AGPL3+"
  4. __version__ = "1.0.0"
  5. __maintainer__ = "Bob Mottram"
  6. __email__ = "bob@freedombone.net"
  7. __status__ = "Production"
  8. import os
  9. import json
  10. import time
  11. from pprint import pprint
  12. from utils import removePostFromCache
  13. from utils import urlPermitted
  14. from utils import getNicknameFromActor
  15. from utils import getDomainFromActor
  16. from utils import locatePost
  17. from utils import getCachedPostFilename
  18. from utils import loadJson
  19. from utils import saveJson
  20. from posts import sendSignedJson
  21. from session import postJson
  22. from webfinger import webfingerHandle
  23. from auth import createBasicAuthHeader
  24. from posts import getPersonBox
  25. def undoLikesCollectionEntry(recentPostsCache: {}, \
  26. baseDir: str,postFilename: str,objectUrl: str, \
  27. actor: str,domain: str,debug: bool) -> None:
  28. """Undoes a like for a particular actor
  29. """
  30. postJsonObject=loadJson(postFilename)
  31. if postJsonObject:
  32. # remove any cached version of this post so that the like icon is changed
  33. nickname=getNicknameFromActor(actor)
  34. cachedPostFilename= \
  35. getCachedPostFilename(baseDir,nickname,domain,postJsonObject)
  36. if cachedPostFilename:
  37. if os.path.isfile(cachedPostFilename):
  38. os.remove(cachedPostFilename)
  39. removePostFromCache(postJsonObject,recentPostsCache)
  40. if not postJsonObject.get('type'):
  41. return
  42. if postJsonObject['type']!='Create':
  43. return
  44. if not postJsonObject.get('object'):
  45. if debug:
  46. pprint(postJsonObject)
  47. print('DEBUG: post '+objectUrl+' has no object')
  48. return
  49. if not isinstance(postJsonObject['object'], dict):
  50. return
  51. if not postJsonObject['object'].get('likes'):
  52. return
  53. if not isinstance(postJsonObject['object']['likes'], dict):
  54. return
  55. if not postJsonObject['object']['likes'].get('items'):
  56. return
  57. totalItems=0
  58. if postJsonObject['object']['likes'].get('totalItems'):
  59. totalItems=postJsonObject['object']['likes']['totalItems']
  60. itemFound=False
  61. for likeItem in postJsonObject['object']['likes']['items']:
  62. if likeItem.get('actor'):
  63. if likeItem['actor']==actor:
  64. if debug:
  65. print('DEBUG: like was removed for '+actor)
  66. postJsonObject['object']['likes']['items'].remove(likeItem)
  67. itemFound=True
  68. break
  69. if itemFound:
  70. if totalItems==1:
  71. if debug:
  72. print('DEBUG: likes was removed from post')
  73. del postJsonObject['object']['likes']
  74. else:
  75. postJsonObject['object']['likes']['totalItems']= \
  76. len(postJsonObject['likes']['items'])
  77. saveJson(postJsonObject,postFilename)
  78. def likedByPerson(postJsonObject: {}, nickname: str,domain: str) -> bool:
  79. """Returns True if the given post is liked by the given person
  80. """
  81. if noOfLikes(postJsonObject)==0:
  82. return False
  83. actorMatch=domain+'/users/'+nickname
  84. for item in postJsonObject['object']['likes']['items']:
  85. if item['actor'].endswith(actorMatch):
  86. return True
  87. return False
  88. def noOfLikes(postJsonObject: {}) -> int:
  89. """Returns the number of likes ona given post
  90. """
  91. if not postJsonObject.get('object'):
  92. return 0
  93. if not isinstance(postJsonObject['object'], dict):
  94. return 0
  95. if not postJsonObject['object'].get('likes'):
  96. return 0
  97. if not isinstance(postJsonObject['object']['likes'], dict):
  98. return 0
  99. if not postJsonObject['object']['likes'].get('items'):
  100. postJsonObject['object']['likes']['items']=[]
  101. postJsonObject['object']['likes']['totalItems']=0
  102. return len(postJsonObject['object']['likes']['items'])
  103. def updateLikesCollection(recentPostsCache: {}, \
  104. baseDir: str,postFilename: str, \
  105. objectUrl: str, \
  106. actor: str,domain: str,debug: bool) -> None:
  107. """Updates the likes collection within a post
  108. """
  109. postJsonObject=loadJson(postFilename)
  110. if postJsonObject:
  111. # remove any cached version of this post so that the like icon is changed
  112. nickname=getNicknameFromActor(actor)
  113. cachedPostFilename= \
  114. getCachedPostFilename(baseDir,nickname,domain,postJsonObject)
  115. if cachedPostFilename:
  116. if os.path.isfile(cachedPostFilename):
  117. os.remove(cachedPostFilename)
  118. removePostFromCache(postJsonObject,recentPostsCache)
  119. if not postJsonObject.get('object'):
  120. if debug:
  121. pprint(postJsonObject)
  122. print('DEBUG: post '+objectUrl+' has no object')
  123. return
  124. if not isinstance(postJsonObject['object'], dict):
  125. return
  126. if not objectUrl.endswith('/likes'):
  127. objectUrl=objectUrl+'/likes'
  128. if not postJsonObject['object'].get('likes'):
  129. if debug:
  130. print('DEBUG: Adding initial likes to '+objectUrl)
  131. likesJson = {
  132. "@context": "https://www.w3.org/ns/activitystreams",
  133. 'id': objectUrl,
  134. 'type': 'Collection',
  135. "totalItems": 1,
  136. 'items': [{
  137. 'type': 'Like',
  138. 'actor': actor
  139. }]
  140. }
  141. postJsonObject['object']['likes']=likesJson
  142. else:
  143. if not postJsonObject['object']['likes'].get('items'):
  144. postJsonObject['object']['likes']['items']=[]
  145. for likeItem in postJsonObject['object']['likes']['items']:
  146. if likeItem.get('actor'):
  147. if likeItem['actor']==actor:
  148. return
  149. newLike={
  150. 'type': 'Like',
  151. 'actor': actor
  152. }
  153. postJsonObject['object']['likes']['items'].append(newLike)
  154. postJsonObject['object']['likes']['totalItems']= \
  155. len(postJsonObject['object']['likes']['items'])
  156. if debug:
  157. print('DEBUG: saving post with likes added')
  158. pprint(postJsonObject)
  159. saveJson(postJsonObject,postFilename)
  160. def like(recentPostsCache: {}, \
  161. session,baseDir: str,federationList: [], \
  162. nickname: str,domain: str,port: int, \
  163. ccList: [],httpPrefix: str, \
  164. objectUrl: str,actorLiked: str, \
  165. clientToServer: bool, \
  166. sendThreads: [],postLog: [], \
  167. personCache: {},cachedWebfingers: {}, \
  168. debug: bool,projectVersion: str) -> {}:
  169. """Creates a like
  170. actor is the person doing the liking
  171. 'to' might be a specific person (actor) whose post was liked
  172. object is typically the url of the message which was liked
  173. """
  174. if not urlPermitted(objectUrl,federationList,"inbox:write"):
  175. return None
  176. fullDomain=domain
  177. if port:
  178. if port!=80 and port!=443:
  179. if ':' not in domain:
  180. fullDomain=domain+':'+str(port)
  181. likeTo=[]
  182. if '/statuses/' in objectUrl:
  183. likeTo=[objectUrl.split('/statuses/')[0]]
  184. newLikeJson = {
  185. "@context": "https://www.w3.org/ns/activitystreams",
  186. 'type': 'Like',
  187. 'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname,
  188. 'object': objectUrl
  189. }
  190. if ccList:
  191. if len(ccList)>0:
  192. newLikeJson['cc']=ccList
  193. # Extract the domain and nickname from a statuses link
  194. likedPostNickname=None
  195. likedPostDomain=None
  196. likedPostPort=None
  197. if actorLiked:
  198. likedPostNickname=getNicknameFromActor(actorLiked)
  199. likedPostDomain,likedPostPort=getDomainFromActor(actorLiked)
  200. else:
  201. if '/users/' in objectUrl or \
  202. '/channel/' in objectUrl or \
  203. '/profile/' in objectUrl:
  204. likedPostNickname=getNicknameFromActor(objectUrl)
  205. likedPostDomain,likedPostPort=getDomainFromActor(objectUrl)
  206. if likedPostNickname:
  207. postFilename=locatePost(baseDir,nickname,domain,objectUrl)
  208. if not postFilename:
  209. print('DEBUG: like baseDir: '+baseDir)
  210. print('DEBUG: like nickname: '+nickname)
  211. print('DEBUG: like domain: '+domain)
  212. print('DEBUG: like objectUrl: '+objectUrl)
  213. return None
  214. updateLikesCollection(recentPostsCache, \
  215. baseDir,postFilename,objectUrl, \
  216. newLikeJson['actor'],domain,debug)
  217. sendSignedJson(newLikeJson,session,baseDir, \
  218. nickname,domain,port, \
  219. likedPostNickname,likedPostDomain,likedPostPort, \
  220. 'https://www.w3.org/ns/activitystreams#Public', \
  221. httpPrefix,True,clientToServer,federationList, \
  222. sendThreads,postLog,cachedWebfingers,personCache, \
  223. debug,projectVersion)
  224. return newLikeJson
  225. def likePost(recentPostsCache: {}, \
  226. session,baseDir: str,federationList: [], \
  227. nickname: str,domain: str,port: int,httpPrefix: str, \
  228. likeNickname: str,likeDomain: str,likePort: int, \
  229. ccList: [], \
  230. likeStatusNumber: int,clientToServer: bool, \
  231. sendThreads: [],postLog: [], \
  232. personCache: {},cachedWebfingers: {}, \
  233. debug: bool,projectVersion: str) -> {}:
  234. """Likes a given status post. This is only used by unit tests
  235. """
  236. likeDomain=likeDomain
  237. if likePort:
  238. if likePort!=80 and likePort!=443:
  239. if ':' not in likeDomain:
  240. likeDomain=likeDomain+':'+str(likePort)
  241. actorLiked= \
  242. httpPrefix + '://'+likeDomain+'/users/'+likeNickname
  243. objectUrl=actorLiked+'/statuses/'+str(likeStatusNumber)
  244. ccUrl=httpPrefix+'://'+likeDomain+'/users/'+likeNickname
  245. if likePort:
  246. if likePort!=80 and likePort!=443:
  247. if ':' not in likeDomain:
  248. ccUrl= \
  249. httpPrefix+'://'+likeDomain+':'+ \
  250. str(likePort)+'/users/'+likeNickname
  251. return like(recentPostsCache, \
  252. session,baseDir,federationList,nickname,domain,port, \
  253. ccList,httpPrefix,objectUrl,actorLiked,clientToServer, \
  254. sendThreads,postLog,personCache,cachedWebfingers, \
  255. debug,projectVersion)
  256. def undolike(recentPostsCache: {}, \
  257. session,baseDir: str,federationList: [], \
  258. nickname: str,domain: str,port: int, \
  259. ccList: [],httpPrefix: str, \
  260. objectUrl: str,actorLiked: str, \
  261. clientToServer: bool, \
  262. sendThreads: [],postLog: [], \
  263. personCache: {},cachedWebfingers: {}, \
  264. debug: bool,projectVersion: str) -> {}:
  265. """Removes a like
  266. actor is the person doing the liking
  267. 'to' might be a specific person (actor) whose post was liked
  268. object is typically the url of the message which was liked
  269. """
  270. if not urlPermitted(objectUrl,federationList,"inbox:write"):
  271. return None
  272. fullDomain=domain
  273. if port:
  274. if port!=80 and port!=443:
  275. if ':' not in domain:
  276. fullDomain=domain+':'+str(port)
  277. likeTo=[]
  278. if '/statuses/' in objectUrl:
  279. likeTo=[objectUrl.split('/statuses/')[0]]
  280. newUndoLikeJson = {
  281. "@context": "https://www.w3.org/ns/activitystreams",
  282. 'type': 'Undo',
  283. 'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname,
  284. 'object': {
  285. 'type': 'Like',
  286. 'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname,
  287. 'object': objectUrl
  288. }
  289. }
  290. if ccList:
  291. if len(ccList)>0:
  292. newUndoLikeJson['cc']=ccList
  293. newUndoLikeJson['object']['cc']=ccList
  294. # Extract the domain and nickname from a statuses link
  295. likedPostNickname=None
  296. likedPostDomain=None
  297. likedPostPort=None
  298. if actorLiked:
  299. likedPostNickname=getNicknameFromActor(actorLiked)
  300. likedPostDomain,likedPostPort=getDomainFromActor(actorLiked)
  301. else:
  302. if '/users/' in objectUrl or \
  303. '/channel/' in objectUrl or \
  304. '/profile/' in objectUrl:
  305. likedPostNickname=getNicknameFromActor(objectUrl)
  306. likedPostDomain,likedPostPort=getDomainFromActor(objectUrl)
  307. if likedPostNickname:
  308. postFilename=locatePost(baseDir,nickname,domain,objectUrl)
  309. if not postFilename:
  310. return None
  311. undoLikesCollectionEntry(baseDir,postFilename,objectUrl, \
  312. newLikeJson['actor'],domain,debug)
  313. sendSignedJson(newUndoLikeJson,session,baseDir, \
  314. nickname,domain,port, \
  315. likedPostNickname,likedPostDomain,likedPostPort, \
  316. 'https://www.w3.org/ns/activitystreams#Public', \
  317. httpPrefix,True,clientToServer,federationList, \
  318. sendThreads,postLog,cachedWebfingers,personCache, \
  319. debug,projectVersion)
  320. else:
  321. return None
  322. return newUndoLikeJson
  323. def undoLikePost(recentPostsCache: {}, \
  324. session,baseDir: str,federationList: [], \
  325. nickname: str,domain: str,port: int,httpPrefix: str, \
  326. likeNickname: str,likeDomain: str,likePort: int, \
  327. ccList: [], \
  328. likeStatusNumber: int,clientToServer: bool, \
  329. sendThreads: [],postLog: [], \
  330. personCache: {},cachedWebfingers: {}, \
  331. debug: bool) -> {}:
  332. """Removes a liked post
  333. """
  334. likeDomain=likeDomain
  335. if likePort:
  336. if likePort!=80 and likePort!=443:
  337. if ':' not in likeDomain:
  338. likeDomain=likeDomain+':'+str(likePort)
  339. objectUrl = \
  340. httpPrefix + '://'+likeDomain+'/users/'+likeNickname+ \
  341. '/statuses/'+str(likeStatusNumber)
  342. ccUrl=httpPrefix+'://'+likeDomain+'/users/'+likeNickname
  343. if likePort:
  344. if likePort!=80 and likePort!=443:
  345. if ':' not in likeDomain:
  346. ccUrl= \
  347. httpPrefix+'://'+likeDomain+':'+ \
  348. str(likePort)+'/users/'+likeNickname
  349. return undoLike(recentPostsCache, \
  350. session,baseDir,federationList,nickname,domain,port, \
  351. ccList,httpPrefix,objectUrl,clientToServer, \
  352. sendThreads,postLog,personCache,cachedWebfingers,debug)
  353. def sendLikeViaServer(baseDir: str,session, \
  354. fromNickname: str,password: str,
  355. fromDomain: str,fromPort: int, \
  356. httpPrefix: str,likeUrl: str, \
  357. cachedWebfingers: {},personCache: {}, \
  358. debug: bool,projectVersion: str) -> {}:
  359. """Creates a like via c2s
  360. """
  361. if not session:
  362. print('WARN: No session for sendLikeViaServer')
  363. return 6
  364. fromDomainFull=fromDomain
  365. if fromPort:
  366. if fromPort!=80 and fromPort!=443:
  367. if ':' not in fromDomain:
  368. fromDomainFull=fromDomain+':'+str(fromPort)
  369. toUrl=['https://www.w3.org/ns/activitystreams#Public']
  370. ccUrl=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname+'/followers'
  371. if '/statuses/' in likeUrl:
  372. toUrl=[likeUrl.split('/statuses/')[0]]
  373. newLikeJson = {
  374. "@context": "https://www.w3.org/ns/activitystreams",
  375. 'type': 'Like',
  376. 'actor': httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname,
  377. 'object': likeUrl
  378. }
  379. handle=httpPrefix+'://'+fromDomainFull+'/@'+fromNickname
  380. # lookup the inbox for the To handle
  381. wfRequest=webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \
  382. fromDomain,projectVersion)
  383. if not wfRequest:
  384. if debug:
  385. print('DEBUG: announce webfinger failed for '+handle)
  386. return 1
  387. postToBox='outbox'
  388. # get the actor inbox for the To handle
  389. inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \
  390. getPersonBox(baseDir,session,wfRequest,personCache, \
  391. projectVersion,httpPrefix,fromNickname, \
  392. fromDomain,postToBox)
  393. if not inboxUrl:
  394. if debug:
  395. print('DEBUG: No '+postToBox+' was found for '+handle)
  396. return 3
  397. if not fromPersonId:
  398. if debug:
  399. print('DEBUG: No actor was found for '+handle)
  400. return 4
  401. authHeader=createBasicAuthHeader(fromNickname,password)
  402. headers = {'host': fromDomain, \
  403. 'Content-type': 'application/json', \
  404. 'Authorization': authHeader}
  405. postResult = \
  406. postJson(session,newLikeJson,[],inboxUrl,headers,"inbox:write")
  407. #if not postResult:
  408. # if debug:
  409. # print('DEBUG: POST announce failed for c2s to '+inboxUrl)
  410. # return 5
  411. if debug:
  412. print('DEBUG: c2s POST like success')
  413. return newLikeJson
  414. def sendUndoLikeViaServer(baseDir: str,session, \
  415. fromNickname: str,password: str, \
  416. fromDomain: str,fromPort: int, \
  417. httpPrefix: str,likeUrl: str, \
  418. cachedWebfingers: {},personCache: {}, \
  419. debug: bool,projectVersion: str) -> {}:
  420. """Undo a like via c2s
  421. """
  422. if not session:
  423. print('WARN: No session for sendUndoLikeViaServer')
  424. return 6
  425. fromDomainFull=fromDomain
  426. if fromPort:
  427. if fromPort!=80 and fromPort!=443:
  428. if ':' not in fromDomain:
  429. fromDomainFull=fromDomain+':'+str(fromPort)
  430. toUrl=['https://www.w3.org/ns/activitystreams#Public']
  431. ccUrl=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname+'/followers'
  432. if '/statuses/' in likeUrl:
  433. toUrl=[likeUrl.split('/statuses/')[0]]
  434. newUndoLikeJson = {
  435. "@context": "https://www.w3.org/ns/activitystreams",
  436. 'type': 'Undo',
  437. 'actor': httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname,
  438. 'object': {
  439. 'type': 'Like',
  440. 'actor': httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname,
  441. 'object': likeUrl
  442. }
  443. }
  444. handle=httpPrefix+'://'+fromDomainFull+'/@'+fromNickname
  445. # lookup the inbox for the To handle
  446. wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \
  447. fromDomain,projectVersion)
  448. if not wfRequest:
  449. if debug:
  450. print('DEBUG: announce webfinger failed for '+handle)
  451. return 1
  452. postToBox='outbox'
  453. # get the actor inbox for the To handle
  454. inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \
  455. getPersonBox(baseDir,session,wfRequest,personCache, \
  456. projectVersion,httpPrefix,fromNickname, \
  457. fromDomain,postToBox)
  458. if not inboxUrl:
  459. if debug:
  460. print('DEBUG: No '+postToBox+' was found for '+handle)
  461. return 3
  462. if not fromPersonId:
  463. if debug:
  464. print('DEBUG: No actor was found for '+handle)
  465. return 4
  466. authHeader=createBasicAuthHeader(fromNickname,password)
  467. headers = {'host': fromDomain, \
  468. 'Content-type': 'application/json', \
  469. 'Authorization': authHeader}
  470. postResult = \
  471. postJson(session,newUndoLikeJson,[],inboxUrl,headers,"inbox:write")
  472. #if not postResult:
  473. # if debug:
  474. # print('DEBUG: POST announce failed for c2s to '+inboxUrl)
  475. # return 5
  476. if debug:
  477. print('DEBUG: c2s POST undo like success')
  478. return newUndoLikeJson
  479. def outboxLike(recentPostsCache: {}, \
  480. baseDir: str,httpPrefix: str, \
  481. nickname: str,domain: str,port: int, \
  482. messageJson: {},debug: bool) -> None:
  483. """ When a like request is received by the outbox from c2s
  484. """
  485. if not messageJson.get('type'):
  486. if debug:
  487. print('DEBUG: like - no type')
  488. return
  489. if not messageJson['type']=='Like':
  490. if debug:
  491. print('DEBUG: not a like')
  492. return
  493. if not messageJson.get('object'):
  494. if debug:
  495. print('DEBUG: no object in like')
  496. return
  497. if not isinstance(messageJson['object'], str):
  498. if debug:
  499. print('DEBUG: like object is not string')
  500. return
  501. if debug:
  502. print('DEBUG: c2s like request arrived in outbox')
  503. messageId=messageJson['object'].replace('/activity','')
  504. if ':' in domain:
  505. domain=domain.split(':')[0]
  506. postFilename=locatePost(baseDir,nickname,domain,messageId)
  507. if not postFilename:
  508. if debug:
  509. print('DEBUG: c2s like post not found in inbox or outbox')
  510. print(messageId)
  511. return True
  512. updateLikesCollection(recentPostsCache, \
  513. baseDir,postFilename,messageId, \
  514. messageJson['actor'],domain,debug)
  515. if debug:
  516. print('DEBUG: post liked via c2s - '+postFilename)
  517. def outboxUndoLike(baseDir: str,httpPrefix: str, \
  518. nickname: str,domain: str,port: int, \
  519. messageJson: {},debug: bool) -> None:
  520. """ When an undo like request is received by the outbox from c2s
  521. """
  522. if not messageJson.get('type'):
  523. return
  524. if not messageJson['type']=='Undo':
  525. return
  526. if not messageJson.get('object'):
  527. return
  528. if not isinstance(messageJson['object'], dict):
  529. if debug:
  530. print('DEBUG: undo like object is not dict')
  531. return
  532. if not messageJson['object'].get('type'):
  533. if debug:
  534. print('DEBUG: undo like - no type')
  535. return
  536. if not messageJson['object']['type']=='Like':
  537. if debug:
  538. print('DEBUG: not a undo like')
  539. return
  540. if not messageJson['object'].get('object'):
  541. if debug:
  542. print('DEBUG: no object in undo like')
  543. return
  544. if not isinstance(messageJson['object']['object'], str):
  545. if debug:
  546. print('DEBUG: undo like object is not string')
  547. return
  548. if debug:
  549. print('DEBUG: c2s undo like request arrived in outbox')
  550. messageId=messageJson['object']['object'].replace('/activity','')
  551. if ':' in domain:
  552. domain=domain.split(':')[0]
  553. postFilename=locatePost(baseDir,nickname,domain,messageId)
  554. if not postFilename:
  555. if debug:
  556. print('DEBUG: c2s undo like post not found in inbox or outbox')
  557. print(messageId)
  558. return True
  559. undoLikesCollectionEntry(baseDir,postFilename,messageId, \
  560. messageJson['actor'],domain,debug)
  561. if debug:
  562. print('DEBUG: post undo liked via c2s - '+postFilename)