2021-03-17 10:04:49 +00:00
|
|
|
__filename__ = "desktop_client.py"
|
2021-03-04 13:57:30 +00:00
|
|
|
__author__ = "Bob Mottram"
|
|
|
|
__license__ = "AGPL3+"
|
|
|
|
__version__ = "1.2.0"
|
|
|
|
__maintainer__ = "Bob Mottram"
|
|
|
|
__email__ = "bob@freedombone.net"
|
|
|
|
__status__ = "Production"
|
|
|
|
|
|
|
|
import os
|
|
|
|
import html
|
|
|
|
import time
|
2021-03-04 15:21:38 +00:00
|
|
|
import sys
|
|
|
|
import select
|
2021-03-15 14:53:21 +00:00
|
|
|
import webbrowser
|
2021-03-18 16:53:32 +00:00
|
|
|
import urllib.parse
|
2021-03-11 20:33:45 +00:00
|
|
|
from random import randint
|
2021-03-18 19:43:10 +00:00
|
|
|
from utils import isDM
|
2021-03-18 17:27:46 +00:00
|
|
|
from utils import loadTranslationsFromFile
|
2021-03-18 16:53:32 +00:00
|
|
|
from utils import removeHtml
|
2021-03-10 13:38:11 +00:00
|
|
|
from utils import getNicknameFromActor
|
|
|
|
from utils import getDomainFromActor
|
2021-03-12 12:04:34 +00:00
|
|
|
from utils import isPGPEncrypted
|
2021-03-04 13:57:30 +00:00
|
|
|
from session import createSession
|
2021-03-18 17:27:46 +00:00
|
|
|
from speaker import speakableText
|
2021-03-04 13:57:30 +00:00
|
|
|
from speaker import getSpeakerPitch
|
|
|
|
from speaker import getSpeakerRate
|
|
|
|
from speaker import getSpeakerRange
|
2021-03-10 13:04:22 +00:00
|
|
|
from like import sendLikeViaServer
|
2021-03-10 13:07:24 +00:00
|
|
|
from like import sendUndoLikeViaServer
|
2021-03-10 13:38:11 +00:00
|
|
|
from follow import sendFollowRequestViaServer
|
|
|
|
from follow import sendUnfollowRequestViaServer
|
2021-03-10 19:06:39 +00:00
|
|
|
from posts import sendPostViaServer
|
2021-03-18 16:53:32 +00:00
|
|
|
from posts import c2sBoxJson
|
2021-03-19 13:54:30 +00:00
|
|
|
from posts import downloadAnnounce
|
2021-03-10 21:45:34 +00:00
|
|
|
from announce import sendAnnounceViaServer
|
2021-03-18 20:56:08 +00:00
|
|
|
from announce import sendUndoAnnounceViaServer
|
2021-03-11 17:15:32 +00:00
|
|
|
from pgp import pgpDecrypt
|
2021-03-11 20:33:45 +00:00
|
|
|
from pgp import hasLocalPGPkey
|
|
|
|
from pgp import pgpEncryptToActor
|
2021-03-17 20:18:00 +00:00
|
|
|
from pgp import pgpPublicKeyUpload
|
2021-03-18 20:04:49 +00:00
|
|
|
from like import noOfLikes
|
2021-03-19 22:04:57 +00:00
|
|
|
from bookmarks import sendBookmarkViaServer
|
2021-03-19 22:11:45 +00:00
|
|
|
from bookmarks import sendUndoBookmarkViaServer
|
2021-03-04 14:36:24 +00:00
|
|
|
|
|
|
|
|
2021-03-16 23:10:14 +00:00
|
|
|
def _desktopHelp() -> None:
|
|
|
|
"""Shows help
|
|
|
|
"""
|
2021-03-16 23:14:34 +00:00
|
|
|
indent = ' '
|
2021-03-16 23:10:14 +00:00
|
|
|
print('')
|
2021-03-16 23:14:34 +00:00
|
|
|
print(indent + 'Commands:')
|
2021-03-16 23:10:14 +00:00
|
|
|
print('')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'quit ' +
|
2021-03-17 10:04:49 +00:00
|
|
|
'Exit from the desktop client')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'show dm|sent|inbox|replies|bookmarks ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Show a timeline')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'mute ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Turn off the screen reader')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'speak ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Turn on the screen reader')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'sounds on ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Turn on notification sounds')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'sounds off ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Turn off notification sounds')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'rp ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Repeat the last post')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'like ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Like the last post')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'unlike ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Unlike the last post')
|
2021-03-20 09:49:43 +00:00
|
|
|
print(indent + 'bookmark ' +
|
|
|
|
'bookmark the last post')
|
|
|
|
print(indent + 'unbookmark ' +
|
|
|
|
'Unbookmark the last post')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'reply ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Reply to the last post')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'post ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Create a new post')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'post to [handle] ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Create a new direct message')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'announce/boost ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Boost the last post')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'follow [handle] ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Make a follow request')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'unfollow [handle] ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Stop following the give handle')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'next ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Next page in the timeline')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'prev ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Previous page in the timeline')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'read [post number] ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Read a post from a timeline')
|
2021-03-19 21:34:38 +00:00
|
|
|
print(indent + 'open [post number] ' +
|
2021-03-16 23:14:34 +00:00
|
|
|
'Open web links within a timeline post')
|
2021-03-16 23:10:14 +00:00
|
|
|
print('')
|
|
|
|
|
|
|
|
|
2021-03-17 10:04:49 +00:00
|
|
|
def _desktopClearScreen() -> None:
|
2021-03-15 14:30:59 +00:00
|
|
|
"""Clears the screen
|
|
|
|
"""
|
2021-03-15 13:35:12 +00:00
|
|
|
os.system('cls' if os.name == 'nt' else 'clear')
|
2021-03-15 13:32:15 +00:00
|
|
|
|
|
|
|
|
2021-03-17 10:04:49 +00:00
|
|
|
def _desktopShowBanner() -> None:
|
2021-03-15 14:30:59 +00:00
|
|
|
"""Shows the banner at the top
|
|
|
|
"""
|
2021-03-15 14:36:03 +00:00
|
|
|
bannerFilename = 'banner.txt'
|
2021-03-15 14:31:33 +00:00
|
|
|
if not os.path.isfile(bannerFilename):
|
2021-03-15 14:36:03 +00:00
|
|
|
bannerTheme = 'starlight'
|
|
|
|
bannerFilename = 'theme/' + bannerTheme + '/banner.txt'
|
|
|
|
if not os.path.isfile(bannerFilename):
|
|
|
|
return
|
2021-03-15 14:31:33 +00:00
|
|
|
with open(bannerFilename, 'r') as bannerFile:
|
|
|
|
banner = bannerFile.read()
|
|
|
|
if banner:
|
|
|
|
print(banner + '\n')
|
2021-03-15 14:30:59 +00:00
|
|
|
|
|
|
|
|
2021-03-17 10:04:49 +00:00
|
|
|
def _desktopWaitForCmd(timeout: int, debug: bool) -> str:
|
|
|
|
"""Waits for a command to be entered with a timeout
|
|
|
|
Returns the command, or None on timeout
|
2021-03-04 14:36:24 +00:00
|
|
|
"""
|
2021-03-04 15:21:38 +00:00
|
|
|
i, o, e = select.select([sys.stdin], [], [], timeout)
|
|
|
|
|
|
|
|
if (i):
|
2021-03-04 15:27:27 +00:00
|
|
|
text = sys.stdin.readline().strip()
|
2021-03-04 15:21:38 +00:00
|
|
|
if debug:
|
2021-03-04 15:27:27 +00:00
|
|
|
print("Text entered: " + text)
|
|
|
|
return text
|
2021-03-04 15:21:38 +00:00
|
|
|
else:
|
2021-03-04 14:36:24 +00:00
|
|
|
if debug:
|
2021-03-04 15:21:38 +00:00
|
|
|
print("Timeout")
|
2021-03-04 14:36:24 +00:00
|
|
|
return None
|
2021-03-04 13:57:30 +00:00
|
|
|
|
|
|
|
|
2021-03-04 14:54:30 +00:00
|
|
|
def _speakerEspeak(espeak, pitch: int, rate: int, srange: int,
|
|
|
|
sayText: str) -> None:
|
|
|
|
"""Speaks the given text with espeak
|
|
|
|
"""
|
|
|
|
espeak.set_parameter(espeak.Parameter.Pitch, pitch)
|
|
|
|
espeak.set_parameter(espeak.Parameter.Rate, rate)
|
|
|
|
espeak.set_parameter(espeak.Parameter.Range, srange)
|
|
|
|
espeak.synth(html.unescape(sayText))
|
|
|
|
|
|
|
|
|
|
|
|
def _speakerPicospeaker(pitch: int, rate: int, systemLanguage: str,
|
|
|
|
sayText: str) -> None:
|
2021-03-16 10:37:51 +00:00
|
|
|
"""TTS using picospeaker
|
|
|
|
"""
|
2021-03-04 14:54:30 +00:00
|
|
|
speakerLang = 'en-GB'
|
|
|
|
if systemLanguage:
|
|
|
|
if systemLanguage.startswith('fr'):
|
|
|
|
speakerLang = 'fr-FR'
|
|
|
|
elif systemLanguage.startswith('es'):
|
|
|
|
speakerLang = 'es-ES'
|
|
|
|
elif systemLanguage.startswith('de'):
|
|
|
|
speakerLang = 'de-DE'
|
|
|
|
elif systemLanguage.startswith('it'):
|
|
|
|
speakerLang = 'it-IT'
|
|
|
|
speakerCmd = 'picospeaker ' + \
|
|
|
|
'-l ' + speakerLang + \
|
|
|
|
' -r ' + str(rate) + \
|
|
|
|
' -p ' + str(pitch) + ' "' + \
|
2021-03-10 12:26:10 +00:00
|
|
|
html.unescape(sayText) + '" 2> /dev/null'
|
2021-03-04 14:54:30 +00:00
|
|
|
os.system(speakerCmd)
|
|
|
|
|
|
|
|
|
2021-03-09 19:52:10 +00:00
|
|
|
def _playNotificationSound(soundFilename: str, player='ffplay') -> None:
|
|
|
|
"""Plays a sound
|
|
|
|
"""
|
|
|
|
if not os.path.isfile(soundFilename):
|
|
|
|
return
|
|
|
|
|
|
|
|
if player == 'ffplay':
|
|
|
|
os.system('ffplay ' + soundFilename +
|
2021-03-10 15:43:21 +00:00
|
|
|
' -autoexit -hide_banner -nodisp 2> /dev/null')
|
2021-03-09 19:52:10 +00:00
|
|
|
|
|
|
|
|
2021-03-09 20:32:50 +00:00
|
|
|
def _desktopNotification(notificationType: str,
|
2021-03-09 21:23:32 +00:00
|
|
|
title: str, message: str) -> None:
|
2021-03-09 20:32:50 +00:00
|
|
|
"""Shows a desktop notification
|
|
|
|
"""
|
|
|
|
if not notificationType:
|
|
|
|
return
|
|
|
|
|
|
|
|
if notificationType == 'notify-send':
|
|
|
|
# Ubuntu
|
|
|
|
os.system('notify-send "' + title + '" "' + message + '"')
|
2021-03-11 10:01:05 +00:00
|
|
|
elif notificationType == 'zenity':
|
|
|
|
# Zenity
|
|
|
|
os.system('zenity --notification --title "' + title +
|
|
|
|
'" --text="' + message + '"')
|
2021-03-09 20:32:50 +00:00
|
|
|
elif notificationType == 'osascript':
|
|
|
|
# Mac
|
|
|
|
os.system("osascript -e 'display notification \"" +
|
|
|
|
message + "\" with title \"" + title + "\"'")
|
|
|
|
elif notificationType == 'New-BurntToastNotification':
|
|
|
|
# Windows
|
|
|
|
os.system("New-BurntToastNotification -Text \"" +
|
|
|
|
title + "\", '" + message + "'")
|
|
|
|
|
|
|
|
|
2021-03-10 12:11:42 +00:00
|
|
|
def _textToSpeech(sayStr: str, screenreader: str,
|
|
|
|
pitch: int, rate: int, srange: int,
|
|
|
|
systemLanguage: str, espeak=None) -> None:
|
|
|
|
"""Say something via TTS
|
|
|
|
"""
|
|
|
|
# speak the post content
|
|
|
|
if screenreader == 'espeak':
|
|
|
|
_speakerEspeak(espeak, pitch, rate, srange, sayStr)
|
|
|
|
elif screenreader == 'picospeaker':
|
|
|
|
_speakerPicospeaker(pitch, rate,
|
|
|
|
systemLanguage, sayStr)
|
|
|
|
|
|
|
|
|
2021-03-11 10:47:52 +00:00
|
|
|
def _sayCommand(content: str, sayStr: str, screenreader: str,
|
2021-03-10 12:11:42 +00:00
|
|
|
systemLanguage: str,
|
|
|
|
espeak=None,
|
|
|
|
speakerName='screen reader',
|
|
|
|
speakerGender='They/Them') -> None:
|
2021-03-10 10:25:41 +00:00
|
|
|
"""Speaks a command
|
|
|
|
"""
|
2021-03-11 10:47:52 +00:00
|
|
|
print(content)
|
2021-03-10 10:32:08 +00:00
|
|
|
if not screenreader:
|
|
|
|
return
|
2021-03-10 10:25:41 +00:00
|
|
|
|
2021-03-10 12:11:42 +00:00
|
|
|
pitch = getSpeakerPitch(speakerName,
|
|
|
|
screenreader, speakerGender)
|
|
|
|
rate = getSpeakerRate(speakerName, screenreader)
|
|
|
|
srange = getSpeakerRange(speakerName)
|
2021-03-10 10:25:41 +00:00
|
|
|
|
2021-03-10 12:11:42 +00:00
|
|
|
_textToSpeech(sayStr, screenreader,
|
|
|
|
pitch, rate, srange,
|
|
|
|
systemLanguage, espeak)
|
2021-03-10 10:25:41 +00:00
|
|
|
|
|
|
|
|
2021-03-17 10:04:49 +00:00
|
|
|
def _desktopReplyToPost(session, postId: str,
|
|
|
|
baseDir: str, nickname: str, password: str,
|
|
|
|
domain: str, port: int, httpPrefix: str,
|
|
|
|
cachedWebfingers: {}, personCache: {},
|
|
|
|
debug: bool, subject: str,
|
|
|
|
screenreader: str, systemLanguage: str,
|
|
|
|
espeak) -> None:
|
|
|
|
"""Use the desktop client to send a reply to the most recent post
|
2021-03-10 18:30:00 +00:00
|
|
|
"""
|
|
|
|
if '://' not in postId:
|
|
|
|
return
|
|
|
|
toNickname = getNicknameFromActor(postId)
|
|
|
|
toDomain, toPort = getDomainFromActor(postId)
|
|
|
|
sayStr = 'Replying to ' + toNickname + '@' + toDomain
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr,
|
2021-03-10 18:30:00 +00:00
|
|
|
screenreader, systemLanguage, espeak)
|
|
|
|
sayStr = 'Type your reply message, then press Enter.'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-10 18:30:00 +00:00
|
|
|
replyMessage = input()
|
|
|
|
if not replyMessage:
|
|
|
|
sayStr = 'No reply was entered.'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-10 18:30:00 +00:00
|
|
|
return
|
|
|
|
replyMessage = replyMessage.strip()
|
|
|
|
if not replyMessage:
|
|
|
|
sayStr = 'No reply was entered.'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-10 18:30:00 +00:00
|
|
|
return
|
|
|
|
sayStr = 'You entered this reply:'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
|
|
|
_sayCommand(replyMessage, replyMessage, screenreader,
|
|
|
|
systemLanguage, espeak)
|
2021-03-10 18:30:00 +00:00
|
|
|
sayStr = 'Send this reply, yes or no?'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-10 18:30:00 +00:00
|
|
|
yesno = input()
|
|
|
|
if 'y' not in yesno.lower():
|
|
|
|
sayStr = 'Abandoning reply'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-10 18:30:00 +00:00
|
|
|
return
|
2021-03-10 19:06:39 +00:00
|
|
|
ccUrl = None
|
|
|
|
followersOnly = False
|
|
|
|
attach = None
|
|
|
|
mediaType = None
|
|
|
|
attachedImageDescription = None
|
|
|
|
isArticle = False
|
|
|
|
subject = None
|
|
|
|
commentsEnabled = True
|
2021-03-10 20:35:05 +00:00
|
|
|
sayStr = 'Sending reply'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-10 19:31:33 +00:00
|
|
|
if sendPostViaServer(__version__,
|
|
|
|
baseDir, session, nickname, password,
|
|
|
|
domain, port,
|
|
|
|
toNickname, toDomain, toPort, ccUrl,
|
|
|
|
httpPrefix, replyMessage, followersOnly,
|
|
|
|
commentsEnabled, attach, mediaType,
|
|
|
|
attachedImageDescription,
|
|
|
|
cachedWebfingers, personCache, isArticle,
|
|
|
|
debug, postId, postId, subject) == 0:
|
|
|
|
sayStr = 'Reply sent'
|
|
|
|
else:
|
|
|
|
sayStr = 'Reply failed'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-10 18:30:00 +00:00
|
|
|
|
|
|
|
|
2021-03-17 10:04:49 +00:00
|
|
|
def _desktopNewPost(session,
|
|
|
|
baseDir: str, nickname: str, password: str,
|
|
|
|
domain: str, port: int, httpPrefix: str,
|
|
|
|
cachedWebfingers: {}, personCache: {},
|
|
|
|
debug: bool,
|
|
|
|
screenreader: str, systemLanguage: str,
|
|
|
|
espeak) -> None:
|
|
|
|
"""Use the desktop client to create a new post
|
2021-03-10 20:28:43 +00:00
|
|
|
"""
|
|
|
|
sayStr = 'Create new post'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-10 20:28:43 +00:00
|
|
|
sayStr = 'Type your post, then press Enter.'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-10 20:28:43 +00:00
|
|
|
newMessage = input()
|
|
|
|
if not newMessage:
|
|
|
|
sayStr = 'No post was entered.'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-10 20:28:43 +00:00
|
|
|
return
|
|
|
|
newMessage = newMessage.strip()
|
|
|
|
if not newMessage:
|
|
|
|
sayStr = 'No post was entered.'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-10 20:28:43 +00:00
|
|
|
return
|
|
|
|
sayStr = 'You entered this public post:'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
|
|
|
_sayCommand(newMessage, newMessage, screenreader, systemLanguage, espeak)
|
2021-03-10 20:28:43 +00:00
|
|
|
sayStr = 'Send this post, yes or no?'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-10 20:28:43 +00:00
|
|
|
yesno = input()
|
|
|
|
if 'y' not in yesno.lower():
|
|
|
|
sayStr = 'Abandoning new post'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-10 20:28:43 +00:00
|
|
|
return
|
|
|
|
ccUrl = None
|
|
|
|
followersOnly = False
|
|
|
|
attach = None
|
|
|
|
mediaType = None
|
|
|
|
attachedImageDescription = None
|
|
|
|
isArticle = False
|
|
|
|
subject = None
|
|
|
|
commentsEnabled = True
|
|
|
|
subject = None
|
2021-03-10 20:35:05 +00:00
|
|
|
sayStr = 'Sending'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-10 20:28:43 +00:00
|
|
|
if sendPostViaServer(__version__,
|
|
|
|
baseDir, session, nickname, password,
|
|
|
|
domain, port,
|
|
|
|
None, '#Public', port, ccUrl,
|
|
|
|
httpPrefix, newMessage, followersOnly,
|
|
|
|
commentsEnabled, attach, mediaType,
|
|
|
|
attachedImageDescription,
|
|
|
|
cachedWebfingers, personCache, isArticle,
|
|
|
|
debug, None, None, subject) == 0:
|
|
|
|
sayStr = 'Post sent'
|
|
|
|
else:
|
|
|
|
sayStr = 'Post failed'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-10 20:28:43 +00:00
|
|
|
|
|
|
|
|
2021-03-16 12:35:53 +00:00
|
|
|
def _safeMessage(content: str) -> str:
|
|
|
|
"""Removes anything potentially unsafe from a string
|
|
|
|
"""
|
|
|
|
return content.replace('`', '').replace('$(', '$ (')
|
|
|
|
|
|
|
|
|
2021-03-18 16:53:32 +00:00
|
|
|
def _timelineIsEmpty(boxJson: {}) -> bool:
|
|
|
|
"""Returns true if the given timeline is empty
|
|
|
|
"""
|
|
|
|
empty = False
|
|
|
|
if not boxJson:
|
|
|
|
empty = True
|
|
|
|
else:
|
|
|
|
if not isinstance(boxJson, dict):
|
|
|
|
empty = True
|
|
|
|
elif not boxJson.get('orderedItems'):
|
|
|
|
empty = True
|
|
|
|
return empty
|
|
|
|
|
|
|
|
|
|
|
|
def _getFirstItemId(boxJson: {}) -> str:
|
|
|
|
"""Returns the id of the first item in the timeline
|
|
|
|
"""
|
|
|
|
if _timelineIsEmpty(boxJson):
|
|
|
|
return
|
|
|
|
if len(boxJson['orderedItems']) == 0:
|
|
|
|
return
|
|
|
|
return boxJson['orderedItems'][0]['id']
|
|
|
|
|
|
|
|
|
|
|
|
def _textOnlyContent(content: str) -> str:
|
|
|
|
"""Remove formatting from the given string
|
|
|
|
"""
|
|
|
|
content = urllib.parse.unquote_plus(content)
|
|
|
|
content = html.unescape(content)
|
|
|
|
return removeHtml(content)
|
|
|
|
|
|
|
|
|
2021-03-19 14:49:40 +00:00
|
|
|
def _getImageDescription(postJsonObject: {}) -> str:
|
|
|
|
"""Returns a image description/s on a post
|
|
|
|
"""
|
|
|
|
imageDescription = ''
|
|
|
|
if not postJsonObject['object'].get('attachment'):
|
|
|
|
return imageDescription
|
|
|
|
|
|
|
|
attachList = postJsonObject['object']['attachment']
|
|
|
|
if not isinstance(attachList, list):
|
|
|
|
return imageDescription
|
|
|
|
|
|
|
|
# for each attachment
|
|
|
|
for img in attachList:
|
|
|
|
if not isinstance(img, dict):
|
|
|
|
continue
|
|
|
|
if not img.get('name'):
|
|
|
|
continue
|
|
|
|
if not isinstance(img['name'], str):
|
|
|
|
continue
|
|
|
|
messageStr = img['name']
|
|
|
|
if messageStr:
|
|
|
|
messageStr = messageStr.strip()
|
|
|
|
if not messageStr.endswith('.'):
|
|
|
|
imageDescription += messageStr + '. '
|
|
|
|
else:
|
|
|
|
imageDescription += messageStr + ' '
|
|
|
|
return imageDescription
|
|
|
|
|
|
|
|
|
2021-03-19 13:54:30 +00:00
|
|
|
def _readLocalBoxPost(session, nickname: str, domain: str,
|
|
|
|
httpPrefix: str, baseDir: str, boxName: str,
|
2021-03-18 16:53:32 +00:00
|
|
|
pageNumber: int, index: int, boxJson: {},
|
2021-03-15 15:14:54 +00:00
|
|
|
systemLanguage: str,
|
2021-03-18 17:27:46 +00:00
|
|
|
screenreader: str, espeak,
|
|
|
|
translate: {}) -> {}:
|
2021-03-15 15:14:54 +00:00
|
|
|
"""Reads a post from the given timeline
|
|
|
|
Returns the speaker json
|
|
|
|
"""
|
2021-03-18 16:53:32 +00:00
|
|
|
if _timelineIsEmpty(boxJson):
|
2021-03-19 13:54:30 +00:00
|
|
|
return {}
|
2021-03-12 19:55:16 +00:00
|
|
|
|
2021-03-18 16:53:32 +00:00
|
|
|
postJsonObject = _desktopGetBoxPostObject(boxJson, index)
|
|
|
|
if not postJsonObject:
|
2021-03-19 13:54:30 +00:00
|
|
|
return {}
|
2021-03-12 19:55:16 +00:00
|
|
|
gender = 'They/Them'
|
|
|
|
|
2021-03-19 17:23:30 +00:00
|
|
|
boxNameStr = boxName
|
|
|
|
if boxName.startswith('tl'):
|
|
|
|
boxNameStr = boxName[2:]
|
|
|
|
sayStr = 'Reading ' + boxNameStr + ' post ' + str(index) + \
|
2021-03-18 16:53:32 +00:00
|
|
|
' from page ' + str(pageNumber) + '.'
|
2021-03-16 18:08:18 +00:00
|
|
|
sayStr2 = sayStr.replace(' dm ', ' DM ')
|
|
|
|
_sayCommand(sayStr, sayStr2, screenreader, systemLanguage, espeak)
|
2021-03-12 20:51:04 +00:00
|
|
|
|
2021-03-19 13:54:30 +00:00
|
|
|
if postJsonObject['type'] == 'Announce':
|
|
|
|
actor = postJsonObject['actor']
|
|
|
|
nameStr = getNicknameFromActor(actor)
|
|
|
|
recentPostsCache = {}
|
|
|
|
allowLocalNetworkAccess = False
|
|
|
|
YTReplacementDomain = None
|
|
|
|
postJsonObject2 = \
|
|
|
|
downloadAnnounce(session, baseDir,
|
|
|
|
httpPrefix,
|
|
|
|
nickname, domain,
|
|
|
|
postJsonObject,
|
|
|
|
__version__, translate,
|
|
|
|
YTReplacementDomain,
|
|
|
|
allowLocalNetworkAccess,
|
|
|
|
recentPostsCache, False)
|
|
|
|
if postJsonObject2:
|
|
|
|
if postJsonObject2.get('object'):
|
|
|
|
if postJsonObject2['object'].get('attributedTo') and \
|
|
|
|
postJsonObject2['object'].get('content'):
|
|
|
|
actor = postJsonObject2['object']['attributedTo']
|
|
|
|
nameStr += ' ' + translate['announces'] + ' ' + \
|
|
|
|
getNicknameFromActor(actor)
|
|
|
|
sayStr = nameStr
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader,
|
|
|
|
systemLanguage, espeak)
|
|
|
|
if screenreader:
|
|
|
|
time.sleep(2)
|
|
|
|
content = \
|
|
|
|
_textOnlyContent(postJsonObject2['object']['content'])
|
2021-03-19 14:49:40 +00:00
|
|
|
content += _getImageDescription(postJsonObject2)
|
2021-03-19 13:54:30 +00:00
|
|
|
messageStr, detectedLinks = \
|
|
|
|
speakableText(baseDir, content, translate)
|
|
|
|
sayStr = content
|
|
|
|
_sayCommand(sayStr, messageStr, screenreader,
|
|
|
|
systemLanguage, espeak)
|
|
|
|
return postJsonObject2
|
|
|
|
return {}
|
|
|
|
|
|
|
|
actor = postJsonObject['object']['attributedTo']
|
|
|
|
nameStr = getNicknameFromActor(actor)
|
|
|
|
content = _textOnlyContent(postJsonObject['object']['content'])
|
2021-03-19 14:49:40 +00:00
|
|
|
content += _getImageDescription(postJsonObject)
|
2021-03-19 13:54:30 +00:00
|
|
|
|
2021-03-18 16:53:32 +00:00
|
|
|
if isPGPEncrypted(content):
|
2021-03-16 11:56:24 +00:00
|
|
|
sayStr = 'Encrypted message. Please enter your passphrase.'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-18 16:53:32 +00:00
|
|
|
content = pgpDecrypt(content, actor)
|
2021-03-16 11:56:24 +00:00
|
|
|
if isPGPEncrypted(content):
|
|
|
|
sayStr = 'Message could not be decrypted'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
2021-03-19 13:54:30 +00:00
|
|
|
return {}
|
2021-03-16 11:56:24 +00:00
|
|
|
|
2021-03-16 12:38:35 +00:00
|
|
|
content = _safeMessage(content)
|
2021-03-18 19:04:58 +00:00
|
|
|
messageStr, detectedLinks = speakableText(baseDir, content, translate)
|
2021-03-16 12:35:53 +00:00
|
|
|
|
2021-03-18 16:53:32 +00:00
|
|
|
if screenreader:
|
|
|
|
time.sleep(2)
|
2021-03-12 20:51:04 +00:00
|
|
|
|
2021-03-12 19:55:16 +00:00
|
|
|
# say the speaker's name
|
|
|
|
_sayCommand(nameStr, nameStr, screenreader,
|
|
|
|
systemLanguage, espeak,
|
|
|
|
nameStr, gender)
|
|
|
|
|
2021-03-19 17:23:30 +00:00
|
|
|
if postJsonObject['object'].get('inReplyTo'):
|
|
|
|
print('Replying to ' + postJsonObject['object']['inReplyTo'] + '\n')
|
|
|
|
|
2021-03-18 16:53:32 +00:00
|
|
|
if screenreader:
|
|
|
|
time.sleep(2)
|
2021-03-12 19:55:16 +00:00
|
|
|
|
|
|
|
# speak the post content
|
|
|
|
_sayCommand(content, messageStr, screenreader,
|
|
|
|
systemLanguage, espeak,
|
|
|
|
nameStr, gender)
|
2021-03-18 16:53:32 +00:00
|
|
|
return postJsonObject
|
2021-03-12 19:55:16 +00:00
|
|
|
|
|
|
|
|
2021-03-18 16:53:32 +00:00
|
|
|
def _desktopGetBoxPostObject(boxJson: {}, index: int) -> {}:
|
|
|
|
"""Gets the post with the given index from the timeline
|
|
|
|
"""
|
|
|
|
ctr = 0
|
|
|
|
for postJsonObject in boxJson['orderedItems']:
|
2021-03-19 13:54:30 +00:00
|
|
|
if not postJsonObject.get('type'):
|
|
|
|
continue
|
2021-03-18 16:53:32 +00:00
|
|
|
if not postJsonObject.get('object'):
|
|
|
|
continue
|
2021-03-19 13:54:30 +00:00
|
|
|
if postJsonObject['type'] == 'Announce':
|
|
|
|
if not isinstance(postJsonObject['object'], str):
|
|
|
|
continue
|
|
|
|
ctr += 1
|
|
|
|
if ctr == index:
|
|
|
|
return postJsonObject
|
|
|
|
continue
|
2021-03-18 16:53:32 +00:00
|
|
|
if not isinstance(postJsonObject['object'], dict):
|
|
|
|
continue
|
|
|
|
if not postJsonObject['object'].get('published'):
|
|
|
|
continue
|
|
|
|
if not postJsonObject['object'].get('content'):
|
|
|
|
continue
|
|
|
|
ctr += 1
|
|
|
|
if ctr == index:
|
|
|
|
return postJsonObject
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
2021-03-18 22:16:01 +00:00
|
|
|
def _formatPublished(published: str) -> str:
|
|
|
|
"""Formats the published time for display on timeline
|
|
|
|
"""
|
|
|
|
dateStr = published.split('T')[0]
|
|
|
|
monthStr = dateStr.split('-')[1]
|
|
|
|
dayStr = dateStr.split('-')[2]
|
|
|
|
timeStr = published.split('T')[1]
|
|
|
|
hourStr = timeStr.split(':')[0]
|
|
|
|
minStr = timeStr.split(':')[1]
|
|
|
|
return monthStr + '-' + dayStr + ' ' + hourStr + ':' + minStr + 'Z'
|
|
|
|
|
|
|
|
|
2021-03-19 13:54:30 +00:00
|
|
|
def _padToWidth(content: str, width: int) -> str:
|
|
|
|
"""Pads the given string to the given width
|
|
|
|
"""
|
|
|
|
if len(content) > width:
|
|
|
|
content = content[:width]
|
|
|
|
else:
|
|
|
|
while len(content) < width:
|
|
|
|
content += ' '
|
|
|
|
return content
|
|
|
|
|
|
|
|
|
2021-03-19 11:59:14 +00:00
|
|
|
def _desktopShowBox(boxName: str, boxJson: {},
|
|
|
|
screenreader: str, systemLanguage: str, espeak,
|
|
|
|
pageNumber=1,
|
|
|
|
newReplies=False,
|
|
|
|
newDMs=False) -> bool:
|
2021-03-18 16:53:32 +00:00
|
|
|
"""Shows online timeline
|
|
|
|
"""
|
2021-03-19 13:54:30 +00:00
|
|
|
numberWidth = 2
|
|
|
|
nameWidth = 16
|
|
|
|
contentWidth = 50
|
2021-03-18 16:53:32 +00:00
|
|
|
indent = ' '
|
|
|
|
|
|
|
|
# title
|
|
|
|
_desktopClearScreen()
|
|
|
|
_desktopShowBanner()
|
|
|
|
|
|
|
|
notificationIcons = ''
|
2021-03-19 17:23:30 +00:00
|
|
|
if boxName.startswith('tl'):
|
2021-03-19 16:10:14 +00:00
|
|
|
boxNameStr = boxName[2:]
|
2021-03-19 15:39:16 +00:00
|
|
|
else:
|
2021-03-19 16:10:14 +00:00
|
|
|
boxNameStr = boxName
|
|
|
|
titleStr = '\33[7m' + boxNameStr.upper() + '\33[0m'
|
2021-03-18 16:53:32 +00:00
|
|
|
# titleStr += ' page ' + str(pageNumber)
|
|
|
|
if notificationIcons:
|
|
|
|
while len(titleStr) < 95 - len(notificationIcons):
|
|
|
|
titleStr += ' '
|
|
|
|
titleStr += notificationIcons
|
|
|
|
print(indent + titleStr + '\n')
|
|
|
|
|
|
|
|
if _timelineIsEmpty(boxJson):
|
2021-03-19 16:10:14 +00:00
|
|
|
boxStr = boxNameStr
|
2021-03-18 16:53:32 +00:00
|
|
|
if boxName == 'dm':
|
|
|
|
boxStr = 'DM'
|
|
|
|
sayStr = indent + 'You have no ' + boxStr + ' posts yet.'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
|
|
|
print('')
|
|
|
|
return False
|
|
|
|
|
|
|
|
ctr = 1
|
|
|
|
for postJsonObject in boxJson['orderedItems']:
|
2021-03-19 13:54:30 +00:00
|
|
|
if not postJsonObject.get('type'):
|
|
|
|
continue
|
|
|
|
if postJsonObject['type'] == 'Announce':
|
|
|
|
if postJsonObject.get('actor') and \
|
|
|
|
postJsonObject.get('object'):
|
|
|
|
if isinstance(postJsonObject['object'], str):
|
|
|
|
authorActor = postJsonObject['actor']
|
|
|
|
name = getNicknameFromActor(authorActor) + ' ⮌'
|
|
|
|
name = _padToWidth(name, nameWidth)
|
|
|
|
ctrStr = str(ctr)
|
|
|
|
posStr = _padToWidth(ctrStr, numberWidth)
|
|
|
|
published = _formatPublished(postJsonObject['published'])
|
|
|
|
announcedNickname = \
|
|
|
|
getNicknameFromActor(postJsonObject['object'])
|
|
|
|
announcedDomain, announcedPort = \
|
|
|
|
getDomainFromActor(postJsonObject['object'])
|
|
|
|
announcedHandle = announcedNickname + '@' + announcedDomain
|
|
|
|
print(indent + str(posStr) + ' | ' + name + ' | ' +
|
|
|
|
published + ' | ' +
|
|
|
|
_padToWidth(announcedHandle, contentWidth))
|
|
|
|
ctr += 1
|
|
|
|
continue
|
|
|
|
|
2021-03-18 16:53:32 +00:00
|
|
|
if not postJsonObject.get('object'):
|
|
|
|
continue
|
|
|
|
if not isinstance(postJsonObject['object'], dict):
|
|
|
|
continue
|
|
|
|
if not postJsonObject['object'].get('published'):
|
|
|
|
continue
|
|
|
|
if not postJsonObject['object'].get('content'):
|
|
|
|
continue
|
2021-03-19 13:54:30 +00:00
|
|
|
ctrStr = str(ctr)
|
|
|
|
posStr = _padToWidth(ctrStr, numberWidth)
|
|
|
|
|
2021-03-18 16:53:32 +00:00
|
|
|
authorActor = postJsonObject['object']['attributedTo']
|
|
|
|
name = getNicknameFromActor(authorActor)
|
2021-03-19 13:54:30 +00:00
|
|
|
|
|
|
|
# append icons to the end of the name
|
2021-03-18 20:11:28 +00:00
|
|
|
spaceAdded = False
|
2021-03-18 19:43:10 +00:00
|
|
|
if postJsonObject['object'].get('inReplyTo'):
|
2021-03-18 20:11:28 +00:00
|
|
|
if not spaceAdded:
|
|
|
|
spaceAdded = True
|
|
|
|
name += ' '
|
|
|
|
name += '↲'
|
2021-03-18 19:52:17 +00:00
|
|
|
if boxName != 'dm':
|
|
|
|
if isDM(postJsonObject):
|
2021-03-18 20:11:28 +00:00
|
|
|
if not spaceAdded:
|
|
|
|
spaceAdded = True
|
|
|
|
name += ' '
|
2021-03-18 19:52:17 +00:00
|
|
|
name += '📧'
|
2021-03-18 20:04:49 +00:00
|
|
|
likesCount = noOfLikes(postJsonObject)
|
|
|
|
if likesCount > 10:
|
|
|
|
likesCount = 10
|
|
|
|
for like in range(likesCount):
|
2021-03-18 20:11:28 +00:00
|
|
|
if not spaceAdded:
|
|
|
|
spaceAdded = True
|
|
|
|
name += ' '
|
2021-03-18 20:04:49 +00:00
|
|
|
name += '❤'
|
2021-03-19 13:54:30 +00:00
|
|
|
name = _padToWidth(name, nameWidth)
|
|
|
|
|
|
|
|
published = _formatPublished(postJsonObject['published'])
|
|
|
|
|
2021-03-18 16:53:32 +00:00
|
|
|
content = _textOnlyContent(postJsonObject['object']['content'])
|
|
|
|
if isPGPEncrypted(content):
|
|
|
|
content = '🔒' + content
|
|
|
|
elif '://' in content:
|
|
|
|
content = '🔗' + content
|
2021-03-19 13:54:30 +00:00
|
|
|
content = _padToWidth(content, contentWidth)
|
2021-03-18 16:53:32 +00:00
|
|
|
print(indent + str(posStr) + ' | ' + name + ' | ' +
|
|
|
|
published + ' | ' + content)
|
|
|
|
ctr += 1
|
|
|
|
|
|
|
|
print('')
|
|
|
|
|
|
|
|
# say the post number range
|
2021-03-19 16:10:14 +00:00
|
|
|
sayStr = indent + boxNameStr + ' page ' + str(pageNumber) + \
|
2021-03-18 16:53:32 +00:00
|
|
|
' containing ' + str(ctr - 1) + ' posts. '
|
|
|
|
if newDMs and boxName != 'dm':
|
|
|
|
sayStr += \
|
|
|
|
'Use \33[3mshow dm\33[0m to view direct messages.'
|
2021-03-19 16:10:14 +00:00
|
|
|
elif newReplies and boxName != 'tlreplies':
|
2021-03-18 16:53:32 +00:00
|
|
|
sayStr += \
|
|
|
|
'Use \33[3mshow replies\33[0m to view reply posts.'
|
|
|
|
else:
|
|
|
|
sayStr += \
|
|
|
|
'Use the \33[3mnext\33[0m and ' + \
|
|
|
|
'\33[3mprev\33[0m commands to navigate.'
|
|
|
|
sayStr2 = sayStr.replace('\33[3m', '').replace('\33[0m', '')
|
|
|
|
sayStr2 = sayStr2.replace('show dm', 'show DM')
|
|
|
|
sayStr2 = sayStr2.replace('dm post', 'Direct message post')
|
|
|
|
_sayCommand(sayStr, sayStr2, screenreader, systemLanguage, espeak)
|
|
|
|
print('')
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
2021-03-17 10:04:49 +00:00
|
|
|
def _desktopNewDM(session, toHandle: str,
|
|
|
|
baseDir: str, nickname: str, password: str,
|
|
|
|
domain: str, port: int, httpPrefix: str,
|
|
|
|
cachedWebfingers: {}, personCache: {},
|
|
|
|
debug: bool,
|
|
|
|
screenreader: str, systemLanguage: str,
|
|
|
|
espeak) -> None:
|
|
|
|
"""Use the desktop client to create a new direct message
|
2021-03-14 10:28:48 +00:00
|
|
|
which can include multiple destination handles
|
|
|
|
"""
|
|
|
|
if ' ' in toHandle:
|
|
|
|
handlesList = toHandle.split(' ')
|
|
|
|
elif ',' in toHandle:
|
|
|
|
handlesList = toHandle.split(',')
|
|
|
|
elif ';' in toHandle:
|
|
|
|
handlesList = toHandle.split(';')
|
|
|
|
else:
|
|
|
|
handlesList = [toHandle]
|
|
|
|
|
|
|
|
for handle in handlesList:
|
|
|
|
handle = handle.strip()
|
2021-03-17 10:04:49 +00:00
|
|
|
_desktopNewDMbase(session, handle,
|
|
|
|
baseDir, nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
cachedWebfingers, personCache,
|
|
|
|
debug,
|
|
|
|
screenreader, systemLanguage,
|
|
|
|
espeak)
|
2021-03-14 10:28:48 +00:00
|
|
|
|
|
|
|
|
2021-03-17 10:04:49 +00:00
|
|
|
def _desktopNewDMbase(session, toHandle: str,
|
|
|
|
baseDir: str, nickname: str, password: str,
|
|
|
|
domain: str, port: int, httpPrefix: str,
|
|
|
|
cachedWebfingers: {}, personCache: {},
|
|
|
|
debug: bool,
|
|
|
|
screenreader: str, systemLanguage: str,
|
|
|
|
espeak) -> None:
|
|
|
|
"""Use the desktop client to create a new direct message
|
2021-03-11 12:24:20 +00:00
|
|
|
"""
|
|
|
|
toPort = port
|
|
|
|
if '://' in toHandle:
|
|
|
|
toNickname = getNicknameFromActor(toHandle)
|
|
|
|
toDomain, toPort = getDomainFromActor(toHandle)
|
|
|
|
toHandle = toNickname + '@' + toDomain
|
|
|
|
else:
|
|
|
|
if toHandle.startswith('@'):
|
|
|
|
toHandle = toHandle[1:]
|
|
|
|
toNickname = toHandle.split('@')[0]
|
|
|
|
toDomain = toHandle.split('@')[1]
|
|
|
|
|
|
|
|
sayStr = 'Create new direct message to ' + toHandle
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
|
|
|
sayStr = 'Type your direct message, then press Enter.'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
|
|
|
newMessage = input()
|
|
|
|
if not newMessage:
|
|
|
|
sayStr = 'No direct message was entered.'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
|
|
|
return
|
|
|
|
newMessage = newMessage.strip()
|
|
|
|
if not newMessage:
|
|
|
|
sayStr = 'No direct message was entered.'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
|
|
|
return
|
|
|
|
sayStr = 'You entered this direct message to ' + toHandle + ':'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
|
|
|
_sayCommand(newMessage, newMessage, screenreader, systemLanguage, espeak)
|
|
|
|
ccUrl = None
|
|
|
|
followersOnly = False
|
|
|
|
attach = None
|
|
|
|
mediaType = None
|
|
|
|
attachedImageDescription = None
|
|
|
|
isArticle = False
|
|
|
|
subject = None
|
|
|
|
commentsEnabled = True
|
|
|
|
subject = None
|
2021-03-11 20:33:45 +00:00
|
|
|
|
|
|
|
# if there is a local PGP key then attempt to encrypt the DM
|
|
|
|
# using the PGP public key of the recipient
|
|
|
|
if hasLocalPGPkey():
|
|
|
|
sayStr = \
|
|
|
|
'Local PGP key detected...' + \
|
|
|
|
'Fetching PGP public key for ' + toHandle
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
|
|
|
paddedMessage = newMessage
|
|
|
|
if len(paddedMessage) < 32:
|
|
|
|
# add some padding before and after
|
|
|
|
# This is to guard against cribs based on small messages, like "Hi"
|
|
|
|
for before in range(randint(1, 16)):
|
|
|
|
paddedMessage = ' ' + paddedMessage
|
|
|
|
for after in range(randint(1, 16)):
|
|
|
|
paddedMessage += ' '
|
|
|
|
cipherText = \
|
|
|
|
pgpEncryptToActor(paddedMessage, toHandle)
|
|
|
|
if not cipherText:
|
2021-03-11 20:52:35 +00:00
|
|
|
sayStr = \
|
|
|
|
toHandle + ' has no PGP public key. ' + \
|
|
|
|
'Your message will be sent in clear text'
|
2021-03-11 20:33:45 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
|
|
|
else:
|
|
|
|
newMessage = cipherText
|
|
|
|
sayStr = 'Message encrypted'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
|
|
|
|
2021-03-11 20:52:35 +00:00
|
|
|
sayStr = 'Send this direct message, yes or no?'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
|
|
|
yesno = input()
|
|
|
|
if 'y' not in yesno.lower():
|
|
|
|
sayStr = 'Abandoning new direct message'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
|
|
|
return
|
|
|
|
|
2021-03-11 12:24:20 +00:00
|
|
|
sayStr = 'Sending'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
|
|
|
if sendPostViaServer(__version__,
|
|
|
|
baseDir, session, nickname, password,
|
|
|
|
domain, port,
|
|
|
|
toNickname, toDomain, toPort, ccUrl,
|
|
|
|
httpPrefix, newMessage, followersOnly,
|
|
|
|
commentsEnabled, attach, mediaType,
|
|
|
|
attachedImageDescription,
|
|
|
|
cachedWebfingers, personCache, isArticle,
|
|
|
|
debug, None, None, subject) == 0:
|
|
|
|
sayStr = 'Direct message sent'
|
|
|
|
else:
|
|
|
|
sayStr = 'Direct message failed'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
|
|
|
|
|
|
|
|
|
2021-03-17 10:04:49 +00:00
|
|
|
def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
|
|
|
nickname: str, domain: str, port: int,
|
|
|
|
password: str, screenreader: str,
|
|
|
|
systemLanguage: str,
|
|
|
|
notificationSounds: bool,
|
|
|
|
notificationType: str,
|
|
|
|
noKeyPress: bool,
|
|
|
|
storeInboxPosts: bool,
|
|
|
|
showNewPosts: bool,
|
2021-03-18 17:27:46 +00:00
|
|
|
language: str,
|
2021-03-17 10:04:49 +00:00
|
|
|
debug: bool) -> None:
|
|
|
|
"""Runs the desktop and screen reader client,
|
2021-03-09 12:03:50 +00:00
|
|
|
which announces new inbox items
|
2021-03-04 13:57:30 +00:00
|
|
|
"""
|
2021-03-15 14:30:59 +00:00
|
|
|
indent = ' '
|
2021-03-15 13:51:21 +00:00
|
|
|
if showNewPosts:
|
|
|
|
indent = ''
|
|
|
|
|
2021-03-17 10:04:49 +00:00
|
|
|
_desktopClearScreen()
|
|
|
|
_desktopShowBanner()
|
2021-03-14 10:41:21 +00:00
|
|
|
|
2021-03-10 10:25:41 +00:00
|
|
|
espeak = None
|
2021-03-09 19:52:10 +00:00
|
|
|
if screenreader:
|
|
|
|
if screenreader == 'espeak':
|
|
|
|
print('Setting up espeak')
|
|
|
|
from espeak import espeak
|
|
|
|
elif screenreader != 'picospeaker':
|
|
|
|
print(screenreader + ' is not a supported TTS system')
|
|
|
|
return
|
2021-03-04 13:57:30 +00:00
|
|
|
|
2021-03-15 13:51:21 +00:00
|
|
|
sayStr = indent + 'Running ' + screenreader + ' for ' + \
|
|
|
|
nickname + '@' + domain
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader,
|
2021-03-10 10:49:45 +00:00
|
|
|
systemLanguage, espeak)
|
2021-03-09 21:30:23 +00:00
|
|
|
else:
|
2021-03-15 13:51:21 +00:00
|
|
|
print(indent + 'Running desktop notifications for ' +
|
|
|
|
nickname + '@' + domain)
|
2021-03-10 09:57:19 +00:00
|
|
|
if notificationSounds:
|
2021-03-15 13:51:21 +00:00
|
|
|
sayStr = indent + 'Notification sounds on'
|
2021-03-10 09:57:19 +00:00
|
|
|
else:
|
2021-03-15 13:51:21 +00:00
|
|
|
sayStr = indent + 'Notification sounds off'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader,
|
2021-03-10 10:49:45 +00:00
|
|
|
systemLanguage, espeak)
|
2021-03-15 12:59:17 +00:00
|
|
|
|
2021-03-18 16:53:32 +00:00
|
|
|
currTimeline = 'inbox'
|
|
|
|
pageNumber = 1
|
2021-03-10 10:51:06 +00:00
|
|
|
|
2021-03-18 16:53:32 +00:00
|
|
|
postJsonObject = {}
|
2021-03-10 10:25:41 +00:00
|
|
|
originalScreenReader = screenreader
|
2021-03-18 16:53:32 +00:00
|
|
|
# prevSay = ''
|
|
|
|
# prevCalendar = False
|
|
|
|
# prevFollow = False
|
|
|
|
# prevLike = ''
|
|
|
|
# prevShare = False
|
|
|
|
# dmSoundFilename = 'dm.ogg'
|
|
|
|
# replySoundFilename = 'reply.ogg'
|
|
|
|
# calendarSoundFilename = 'calendar.ogg'
|
|
|
|
# followSoundFilename = 'follow.ogg'
|
|
|
|
# likeSoundFilename = 'like.ogg'
|
|
|
|
# shareSoundFilename = 'share.ogg'
|
|
|
|
# player = 'ffplay'
|
2021-03-10 12:37:44 +00:00
|
|
|
nameStr = None
|
|
|
|
gender = None
|
|
|
|
messageStr = None
|
2021-03-11 10:52:06 +00:00
|
|
|
content = None
|
2021-03-10 13:04:22 +00:00
|
|
|
cachedWebfingers = {}
|
|
|
|
personCache = {}
|
2021-03-16 13:30:09 +00:00
|
|
|
newRepliesExist = False
|
2021-03-16 17:43:35 +00:00
|
|
|
newDMsExist = False
|
2021-03-17 20:18:00 +00:00
|
|
|
pgpKeyUpload = False
|
|
|
|
|
2021-03-19 10:14:57 +00:00
|
|
|
# NOTE: These are dummy calls to make unit tests pass
|
|
|
|
# they should be removed later
|
|
|
|
_desktopNotification("", "test", "message")
|
|
|
|
_playNotificationSound("test83639")
|
|
|
|
|
2021-03-18 17:30:47 +00:00
|
|
|
sayStr = indent + 'Loading translations file'
|
2021-03-18 17:27:46 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader,
|
|
|
|
systemLanguage, espeak)
|
|
|
|
translate, systemLanguage = \
|
|
|
|
loadTranslationsFromFile(baseDir, language)
|
|
|
|
|
2021-03-18 16:53:32 +00:00
|
|
|
sayStr = indent + 'Connecting...'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader,
|
|
|
|
systemLanguage, espeak)
|
|
|
|
session = createSession(proxyType)
|
2021-03-18 17:30:47 +00:00
|
|
|
|
|
|
|
sayStr = indent + '/q or /quit to exit'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader,
|
|
|
|
systemLanguage, espeak)
|
2021-03-18 16:53:32 +00:00
|
|
|
prevTimelineFirstId = ''
|
|
|
|
while (1):
|
2021-03-17 20:18:00 +00:00
|
|
|
if not pgpKeyUpload:
|
2021-03-18 17:37:14 +00:00
|
|
|
sayStr = indent + 'Uploading PGP public key'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader,
|
|
|
|
systemLanguage, espeak)
|
2021-03-18 17:42:24 +00:00
|
|
|
pgpPublicKeyUpload(baseDir, session,
|
|
|
|
nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
cachedWebfingers, personCache,
|
|
|
|
debug, False)
|
2021-03-18 17:37:14 +00:00
|
|
|
sayStr = indent + 'PGP public key uploaded'
|
|
|
|
_sayCommand(sayStr, sayStr, screenreader,
|
|
|
|
systemLanguage, espeak)
|
2021-03-17 20:18:00 +00:00
|
|
|
pgpKeyUpload = True
|
|
|
|
|
2021-03-18 16:53:32 +00:00
|
|
|
boxJson = c2sBoxJson(baseDir, session,
|
|
|
|
nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
currTimeline, pageNumber,
|
|
|
|
debug)
|
|
|
|
|
|
|
|
if boxJson:
|
|
|
|
timelineFirstId = _getFirstItemId(boxJson)
|
|
|
|
if timelineFirstId != prevTimelineFirstId:
|
|
|
|
_desktopClearScreen()
|
2021-03-19 11:59:14 +00:00
|
|
|
_desktopShowBox(currTimeline, boxJson,
|
|
|
|
None, systemLanguage, espeak,
|
|
|
|
pageNumber,
|
|
|
|
newRepliesExist,
|
|
|
|
newDMsExist)
|
2021-03-18 16:53:32 +00:00
|
|
|
prevTimelineFirstId = timelineFirstId
|
2021-03-19 15:31:16 +00:00
|
|
|
else:
|
|
|
|
session = createSession(proxyType)
|
2021-03-04 13:57:30 +00:00
|
|
|
|
|
|
|
# wait for a while, or until a key is pressed
|
2021-03-14 18:02:32 +00:00
|
|
|
if noKeyPress:
|
|
|
|
time.sleep(10)
|
|
|
|
else:
|
2021-03-18 16:53:32 +00:00
|
|
|
commandStr = _desktopWaitForCmd(30, debug)
|
2021-03-17 10:04:49 +00:00
|
|
|
if commandStr:
|
|
|
|
if commandStr.startswith('/'):
|
|
|
|
commandStr = commandStr[1:]
|
|
|
|
if commandStr == 'q' or \
|
|
|
|
commandStr == 'quit' or \
|
|
|
|
commandStr == 'exit':
|
2021-03-10 10:46:50 +00:00
|
|
|
sayStr = 'Quit'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader,
|
2021-03-10 10:46:50 +00:00
|
|
|
systemLanguage, espeak)
|
2021-03-10 18:31:45 +00:00
|
|
|
if screenreader:
|
2021-03-17 10:04:49 +00:00
|
|
|
commandStr = _desktopWaitForCmd(2, debug)
|
2021-03-04 18:05:46 +00:00
|
|
|
break
|
2021-03-17 10:04:49 +00:00
|
|
|
elif commandStr.startswith('show dm'):
|
2021-03-18 16:53:32 +00:00
|
|
|
pageNumber = 1
|
|
|
|
prevTimelineFirstId = ''
|
2021-03-12 19:13:01 +00:00
|
|
|
currTimeline = 'dm'
|
2021-03-18 16:53:32 +00:00
|
|
|
boxJson = c2sBoxJson(baseDir, session,
|
|
|
|
nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
currTimeline, pageNumber,
|
|
|
|
debug)
|
|
|
|
if boxJson:
|
2021-03-19 11:59:14 +00:00
|
|
|
_desktopShowBox(currTimeline, boxJson,
|
|
|
|
screenreader, systemLanguage, espeak,
|
|
|
|
pageNumber,
|
|
|
|
newRepliesExist, newDMsExist)
|
2021-03-16 17:43:35 +00:00
|
|
|
newDMsExist = False
|
2021-03-17 10:04:49 +00:00
|
|
|
elif commandStr.startswith('show rep'):
|
2021-03-18 16:53:32 +00:00
|
|
|
pageNumber = 1
|
|
|
|
prevTimelineFirstId = ''
|
2021-03-19 15:39:16 +00:00
|
|
|
currTimeline = 'tlreplies'
|
2021-03-18 16:53:32 +00:00
|
|
|
boxJson = c2sBoxJson(baseDir, session,
|
|
|
|
nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
currTimeline, pageNumber,
|
|
|
|
debug)
|
|
|
|
if boxJson:
|
2021-03-19 11:59:14 +00:00
|
|
|
_desktopShowBox(currTimeline, boxJson,
|
|
|
|
screenreader, systemLanguage, espeak,
|
|
|
|
pageNumber,
|
|
|
|
newRepliesExist, newDMsExist)
|
2021-03-16 13:30:09 +00:00
|
|
|
# Turn off the replies indicator
|
|
|
|
newRepliesExist = False
|
2021-03-19 21:34:38 +00:00
|
|
|
elif commandStr.startswith('show b'):
|
|
|
|
pageNumber = 1
|
|
|
|
prevTimelineFirstId = ''
|
|
|
|
currTimeline = 'tlbookmarks'
|
|
|
|
boxJson = c2sBoxJson(baseDir, session,
|
|
|
|
nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
currTimeline, pageNumber,
|
|
|
|
debug)
|
|
|
|
if boxJson:
|
|
|
|
_desktopShowBox(currTimeline, boxJson,
|
|
|
|
screenreader, systemLanguage, espeak,
|
|
|
|
pageNumber,
|
|
|
|
newRepliesExist, newDMsExist)
|
|
|
|
# Turn off the replies indicator
|
|
|
|
newRepliesExist = False
|
2021-03-19 15:28:16 +00:00
|
|
|
elif (commandStr.startswith('show sen') or
|
|
|
|
commandStr.startswith('show out')):
|
2021-03-18 16:53:32 +00:00
|
|
|
pageNumber = 1
|
|
|
|
prevTimelineFirstId = ''
|
|
|
|
currTimeline = 'outbox'
|
|
|
|
boxJson = c2sBoxJson(baseDir, session,
|
|
|
|
nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
currTimeline, pageNumber,
|
|
|
|
debug)
|
|
|
|
if boxJson:
|
2021-03-19 11:59:14 +00:00
|
|
|
_desktopShowBox(currTimeline, boxJson,
|
|
|
|
screenreader, systemLanguage, espeak,
|
|
|
|
pageNumber,
|
|
|
|
newRepliesExist, newDMsExist)
|
2021-03-17 10:04:49 +00:00
|
|
|
elif (commandStr == 'show' or commandStr.startswith('show in') or
|
|
|
|
commandStr == 'clear'):
|
2021-03-18 16:53:32 +00:00
|
|
|
pageNumber = 1
|
|
|
|
prevTimelineFirstId = ''
|
2021-03-12 19:13:01 +00:00
|
|
|
currTimeline = 'inbox'
|
2021-03-18 16:53:32 +00:00
|
|
|
boxJson = c2sBoxJson(baseDir, session,
|
|
|
|
nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
currTimeline, pageNumber,
|
|
|
|
debug)
|
|
|
|
if boxJson:
|
2021-03-19 11:59:14 +00:00
|
|
|
_desktopShowBox(currTimeline, boxJson,
|
|
|
|
screenreader, systemLanguage, espeak,
|
|
|
|
pageNumber,
|
|
|
|
newRepliesExist, newDMsExist)
|
2021-03-17 10:04:49 +00:00
|
|
|
elif commandStr.startswith('next'):
|
2021-03-18 16:53:32 +00:00
|
|
|
pageNumber += 1
|
|
|
|
prevTimelineFirstId = ''
|
|
|
|
boxJson = c2sBoxJson(baseDir, session,
|
|
|
|
nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
currTimeline, pageNumber,
|
|
|
|
debug)
|
|
|
|
if boxJson:
|
2021-03-19 11:59:14 +00:00
|
|
|
_desktopShowBox(currTimeline, boxJson,
|
|
|
|
screenreader, systemLanguage, espeak,
|
|
|
|
pageNumber,
|
|
|
|
newRepliesExist, newDMsExist)
|
2021-03-17 10:04:49 +00:00
|
|
|
elif commandStr.startswith('prev'):
|
2021-03-18 16:53:32 +00:00
|
|
|
pageNumber -= 1
|
|
|
|
if pageNumber < 1:
|
|
|
|
pageNumber = 1
|
|
|
|
prevTimelineFirstId = ''
|
|
|
|
boxJson = c2sBoxJson(baseDir, session,
|
|
|
|
nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
currTimeline, pageNumber,
|
|
|
|
debug)
|
|
|
|
if boxJson:
|
2021-03-19 11:59:14 +00:00
|
|
|
_desktopShowBox(currTimeline, boxJson,
|
|
|
|
screenreader, systemLanguage, espeak,
|
|
|
|
pageNumber,
|
|
|
|
newRepliesExist, newDMsExist)
|
2021-03-17 10:04:49 +00:00
|
|
|
elif commandStr.startswith('read ') or commandStr == 'read':
|
|
|
|
if commandStr == 'read':
|
2021-03-16 14:06:05 +00:00
|
|
|
postIndexStr = '1'
|
|
|
|
else:
|
2021-03-17 10:04:49 +00:00
|
|
|
postIndexStr = commandStr.split('read ')[1]
|
2021-03-18 16:53:32 +00:00
|
|
|
if boxJson and postIndexStr.isdigit():
|
2021-03-12 19:55:16 +00:00
|
|
|
postIndex = int(postIndexStr)
|
2021-03-18 16:53:32 +00:00
|
|
|
postJsonObject = \
|
2021-03-19 13:54:30 +00:00
|
|
|
_readLocalBoxPost(session, nickname, domain,
|
|
|
|
httpPrefix, baseDir, currTimeline,
|
2021-03-18 16:53:32 +00:00
|
|
|
pageNumber, postIndex, boxJson,
|
2021-03-15 13:14:47 +00:00
|
|
|
systemLanguage, screenreader,
|
2021-03-18 17:27:46 +00:00
|
|
|
espeak, translate)
|
2021-03-12 19:55:16 +00:00
|
|
|
print('')
|
2021-03-17 10:04:49 +00:00
|
|
|
elif commandStr == 'reply' or commandStr == 'r':
|
2021-03-18 16:53:32 +00:00
|
|
|
if postJsonObject:
|
|
|
|
if postJsonObject.get('id'):
|
|
|
|
postId = postJsonObject['id']
|
|
|
|
subject = None
|
|
|
|
if postJsonObject['object'].get('summary'):
|
|
|
|
subject = postJsonObject['object']['summary']
|
|
|
|
sessionReply = createSession(proxyType)
|
|
|
|
_desktopReplyToPost(sessionReply, postId,
|
|
|
|
baseDir, nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
cachedWebfingers, personCache,
|
|
|
|
debug, subject,
|
|
|
|
screenreader, systemLanguage,
|
|
|
|
espeak)
|
2021-03-10 20:28:43 +00:00
|
|
|
print('')
|
2021-03-17 10:04:49 +00:00
|
|
|
elif (commandStr == 'post' or commandStr == 'p' or
|
|
|
|
commandStr == 'send' or
|
|
|
|
commandStr.startswith('dm ') or
|
|
|
|
commandStr.startswith('direct message ') or
|
|
|
|
commandStr.startswith('post ') or
|
|
|
|
commandStr.startswith('send ')):
|
2021-03-10 20:28:43 +00:00
|
|
|
sessionPost = createSession(proxyType)
|
2021-03-17 10:04:49 +00:00
|
|
|
if commandStr.startswith('dm ') or \
|
|
|
|
commandStr.startswith('direct message ') or \
|
|
|
|
commandStr.startswith('post ') or \
|
|
|
|
commandStr.startswith('send '):
|
|
|
|
commandStr = commandStr.replace(' to ', ' ')
|
|
|
|
commandStr = commandStr.replace(' dm ', ' ')
|
|
|
|
commandStr = commandStr.replace(' DM ', ' ')
|
2021-03-11 12:24:20 +00:00
|
|
|
# direct message
|
2021-03-11 12:30:29 +00:00
|
|
|
toHandle = None
|
2021-03-17 10:04:49 +00:00
|
|
|
if commandStr.startswith('post '):
|
|
|
|
toHandle = commandStr.split('post ', 1)[1]
|
|
|
|
elif commandStr.startswith('send '):
|
|
|
|
toHandle = commandStr.split('send ', 1)[1]
|
|
|
|
elif commandStr.startswith('dm '):
|
|
|
|
toHandle = commandStr.split('dm ', 1)[1]
|
|
|
|
elif commandStr.startswith('direct message '):
|
|
|
|
toHandle = commandStr.split('direct message ', 1)[1]
|
2021-03-11 12:30:29 +00:00
|
|
|
if toHandle:
|
2021-03-17 10:04:49 +00:00
|
|
|
_desktopNewDM(sessionPost, toHandle,
|
|
|
|
baseDir, nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
cachedWebfingers, personCache,
|
|
|
|
debug,
|
|
|
|
screenreader, systemLanguage,
|
|
|
|
espeak)
|
2021-03-11 12:24:20 +00:00
|
|
|
else:
|
|
|
|
# public post
|
2021-03-17 10:04:49 +00:00
|
|
|
_desktopNewPost(sessionPost,
|
|
|
|
baseDir, nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
cachedWebfingers, personCache,
|
|
|
|
debug,
|
|
|
|
screenreader, systemLanguage,
|
|
|
|
espeak)
|
2021-03-10 18:30:00 +00:00
|
|
|
print('')
|
2021-03-18 20:56:08 +00:00
|
|
|
elif commandStr == 'like' or commandStr.startswith('like '):
|
|
|
|
currIndex = 0
|
|
|
|
if ' ' in commandStr:
|
|
|
|
postIndex = commandStr.split(' ')[-1].strip()
|
|
|
|
if postIndex.isdigit():
|
|
|
|
currIndex = int(postIndex)
|
|
|
|
if currIndex > 0 and boxJson:
|
|
|
|
postJsonObject = \
|
|
|
|
_desktopGetBoxPostObject(boxJson, currIndex)
|
2021-03-18 16:53:32 +00:00
|
|
|
if postJsonObject:
|
|
|
|
if postJsonObject.get('id'):
|
|
|
|
likeActor = postJsonObject['object']['attributedTo']
|
|
|
|
sayStr = 'Liking post by ' + \
|
|
|
|
getNicknameFromActor(likeActor)
|
|
|
|
_sayCommand(sayStr, sayStr,
|
|
|
|
screenreader,
|
|
|
|
systemLanguage, espeak)
|
|
|
|
sessionLike = createSession(proxyType)
|
|
|
|
sendLikeViaServer(baseDir, sessionLike,
|
2021-03-10 13:07:24 +00:00
|
|
|
nickname, password,
|
2021-03-18 16:53:32 +00:00
|
|
|
domain, port, httpPrefix,
|
|
|
|
postJsonObject['id'],
|
2021-03-10 13:07:24 +00:00
|
|
|
cachedWebfingers, personCache,
|
2021-03-15 12:32:32 +00:00
|
|
|
False, __version__)
|
2021-03-18 16:53:32 +00:00
|
|
|
print('')
|
2021-03-19 22:04:57 +00:00
|
|
|
elif (commandStr == 'bookmark' or
|
|
|
|
commandStr == 'bm' or
|
|
|
|
commandStr.startswith('bookmark ') or
|
|
|
|
commandStr.startswith('bm ')):
|
|
|
|
currIndex = 0
|
|
|
|
if ' ' in commandStr:
|
|
|
|
postIndex = commandStr.split(' ')[-1].strip()
|
|
|
|
if postIndex.isdigit():
|
|
|
|
currIndex = int(postIndex)
|
|
|
|
if currIndex > 0 and boxJson:
|
|
|
|
postJsonObject = \
|
|
|
|
_desktopGetBoxPostObject(boxJson, currIndex)
|
|
|
|
if postJsonObject:
|
|
|
|
if postJsonObject.get('id'):
|
|
|
|
likeActor = postJsonObject['object']['attributedTo']
|
|
|
|
sayStr = 'Bookmarking post by ' + \
|
|
|
|
getNicknameFromActor(likeActor)
|
|
|
|
_sayCommand(sayStr, sayStr,
|
|
|
|
screenreader,
|
|
|
|
systemLanguage, espeak)
|
|
|
|
sessionLike = createSession(proxyType)
|
|
|
|
sendBookmarkViaServer(baseDir, sessionLike,
|
|
|
|
nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
postJsonObject['id'],
|
|
|
|
cachedWebfingers, personCache,
|
|
|
|
False, __version__)
|
|
|
|
print('')
|
2021-03-19 22:11:45 +00:00
|
|
|
elif (commandStr == 'undo bookmark' or
|
|
|
|
commandStr == 'undo bm' or
|
|
|
|
commandStr == 'unbookmark' or
|
|
|
|
commandStr == 'unbm' or
|
|
|
|
commandStr.startswith('unbookmark ') or
|
|
|
|
commandStr.startswith('unbm ')):
|
|
|
|
currIndex = 0
|
|
|
|
if ' ' in commandStr:
|
|
|
|
postIndex = commandStr.split(' ')[-1].strip()
|
|
|
|
if postIndex.isdigit():
|
|
|
|
currIndex = int(postIndex)
|
|
|
|
if currIndex > 0 and boxJson:
|
|
|
|
postJsonObject = \
|
|
|
|
_desktopGetBoxPostObject(boxJson, currIndex)
|
|
|
|
if postJsonObject:
|
|
|
|
if postJsonObject.get('id'):
|
|
|
|
likeActor = postJsonObject['object']['attributedTo']
|
|
|
|
sayStr = 'Unbookmarking post by ' + \
|
|
|
|
getNicknameFromActor(likeActor)
|
|
|
|
_sayCommand(sayStr, sayStr,
|
|
|
|
screenreader,
|
|
|
|
systemLanguage, espeak)
|
|
|
|
sessionLike = createSession(proxyType)
|
|
|
|
sendUndoBookmarkViaServer(baseDir, sessionLike,
|
|
|
|
nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
postJsonObject['id'],
|
|
|
|
cachedWebfingers,
|
|
|
|
personCache,
|
|
|
|
False, __version__)
|
|
|
|
print('')
|
2021-03-18 16:53:32 +00:00
|
|
|
elif commandStr == 'unlike' or commandStr == 'undo like':
|
2021-03-18 20:56:08 +00:00
|
|
|
currIndex = 0
|
|
|
|
if ' ' in commandStr:
|
|
|
|
postIndex = commandStr.split(' ')[-1].strip()
|
|
|
|
if postIndex.isdigit():
|
|
|
|
currIndex = int(postIndex)
|
|
|
|
if currIndex > 0 and boxJson:
|
|
|
|
postJsonObject = \
|
|
|
|
_desktopGetBoxPostObject(boxJson, currIndex)
|
2021-03-18 16:53:32 +00:00
|
|
|
if postJsonObject:
|
|
|
|
if postJsonObject.get('id'):
|
|
|
|
unlikeActor = postJsonObject['object']['attributedTo']
|
|
|
|
sayStr = \
|
|
|
|
'Undoing like of post by ' + \
|
|
|
|
getNicknameFromActor(unlikeActor)
|
|
|
|
_sayCommand(sayStr, sayStr,
|
|
|
|
screenreader,
|
|
|
|
systemLanguage, espeak)
|
|
|
|
sessionUnlike = createSession(proxyType)
|
|
|
|
sendUndoLikeViaServer(baseDir, sessionUnlike,
|
|
|
|
nickname, password,
|
|
|
|
domain, port, httpPrefix,
|
|
|
|
postJsonObject['id'],
|
|
|
|
cachedWebfingers, personCache,
|
|
|
|
False, __version__)
|
|
|
|
print('')
|
2021-03-18 20:56:08 +00:00
|
|
|
elif (commandStr.startswith('announce') or
|
|
|
|
commandStr.startswith('boost') or
|
|
|
|
commandStr.startswith('retweet')):
|
|
|
|
currIndex = 0
|
|
|
|
if ' ' in commandStr:
|
|
|
|
postIndex = commandStr.split(' ')[-1].strip()
|
|
|
|
if postIndex.isdigit():
|
|
|
|
currIndex = int(postIndex)
|
|
|
|
if currIndex > 0 and boxJson:
|
|
|
|
postJsonObject = \
|
|
|
|
_desktopGetBoxPostObject(boxJson, currIndex)
|
2021-03-18 16:53:32 +00:00
|
|
|
if postJsonObject:
|
|
|
|
if postJsonObject.get('id'):
|
|
|
|
postId = postJsonObject['id']
|
|
|
|
announceActor = \
|
|
|
|
postJsonObject['object']['attributedTo']
|
|
|
|
sayStr = 'Announcing post by ' + \
|
|
|
|
getNicknameFromActor(announceActor)
|
|
|
|
_sayCommand(sayStr, sayStr,
|
|
|
|
screenreader,
|
|
|
|
systemLanguage, espeak)
|
|
|
|
sessionAnnounce = createSession(proxyType)
|
|
|
|
sendAnnounceViaServer(baseDir, sessionAnnounce,
|
|
|
|
nickname, password,
|
|
|
|
domain, port,
|
|
|
|
httpPrefix, postId,
|
|
|
|
cachedWebfingers, personCache,
|
|
|
|
True, __version__)
|
|
|
|
print('')
|
2021-03-18 20:56:08 +00:00
|
|
|
elif (commandStr.startswith('unannounce') or
|
|
|
|
commandStr.startswith('undo announce') or
|
|
|
|
commandStr.startswith('unboost') or
|
|
|
|
commandStr.startswith('undo boost') or
|
|
|
|
commandStr.startswith('undo retweet')):
|
|
|
|
currIndex = 0
|
|
|
|
if ' ' in commandStr:
|
|
|
|
postIndex = commandStr.split(' ')[-1].strip()
|
|
|
|
if postIndex.isdigit():
|
|
|
|
currIndex = int(postIndex)
|
|
|
|
if currIndex > 0 and boxJson:
|
|
|
|
postJsonObject = \
|
|
|
|
_desktopGetBoxPostObject(boxJson, currIndex)
|
|
|
|
if postJsonObject:
|
|
|
|
if postJsonObject.get('id'):
|
|
|
|
postId = postJsonObject['id']
|
|
|
|
announceActor = \
|
|
|
|
postJsonObject['object']['attributedTo']
|
|
|
|
sayStr = 'Undoing announce post by ' + \
|
|
|
|
getNicknameFromActor(announceActor)
|
|
|
|
_sayCommand(sayStr, sayStr,
|
|
|
|
screenreader,
|
|
|
|
systemLanguage, espeak)
|
|
|
|
sessionAnnounce = createSession(proxyType)
|
|
|
|
sendUndoAnnounceViaServer(baseDir, sessionAnnounce,
|
|
|
|
postJsonObject,
|
|
|
|
nickname, password,
|
|
|
|
domain, port,
|
|
|
|
httpPrefix, postId,
|
|
|
|
cachedWebfingers,
|
|
|
|
personCache,
|
|
|
|
True, __version__)
|
|
|
|
print('')
|
2021-03-17 10:04:49 +00:00
|
|
|
elif commandStr.startswith('follow '):
|
|
|
|
followHandle = commandStr.replace('follow ', '').strip()
|
2021-03-10 13:38:11 +00:00
|
|
|
if followHandle.startswith('@'):
|
|
|
|
followHandle = followHandle[1:]
|
|
|
|
if '@' in followHandle or '://' in followHandle:
|
|
|
|
followNickname = getNicknameFromActor(followHandle)
|
|
|
|
followDomain, followPort = \
|
|
|
|
getDomainFromActor(followHandle)
|
|
|
|
if followNickname and followDomain:
|
2021-03-11 10:47:52 +00:00
|
|
|
sayStr = 'Sending follow request to ' + \
|
|
|
|
followNickname + '@' + followDomain
|
|
|
|
_sayCommand(sayStr, sayStr,
|
2021-03-10 13:38:11 +00:00
|
|
|
screenreader, systemLanguage, espeak)
|
2021-03-10 16:56:27 +00:00
|
|
|
sessionFollow = createSession(proxyType)
|
|
|
|
sendFollowRequestViaServer(baseDir, sessionFollow,
|
2021-03-10 13:38:11 +00:00
|
|
|
nickname, password,
|
|
|
|
domain, port,
|
|
|
|
followNickname,
|
|
|
|
followDomain,
|
|
|
|
followPort,
|
|
|
|
httpPrefix,
|
|
|
|
cachedWebfingers,
|
|
|
|
personCache,
|
|
|
|
debug, __version__)
|
|
|
|
else:
|
2021-03-11 10:47:52 +00:00
|
|
|
sayStr = followHandle + ' is not valid'
|
|
|
|
_sayCommand(sayStr,
|
2021-03-10 13:38:11 +00:00
|
|
|
screenreader, systemLanguage, espeak)
|
|
|
|
print('')
|
2021-03-17 10:04:49 +00:00
|
|
|
elif (commandStr.startswith('unfollow ') or
|
|
|
|
commandStr.startswith('stop following ')):
|
|
|
|
followHandle = commandStr.replace('unfollow ', '').strip()
|
2021-03-10 13:38:11 +00:00
|
|
|
followHandle = followHandle.replace('stop following ', '')
|
|
|
|
if followHandle.startswith('@'):
|
|
|
|
followHandle = followHandle[1:]
|
|
|
|
if '@' in followHandle or '://' in followHandle:
|
|
|
|
followNickname = getNicknameFromActor(followHandle)
|
|
|
|
followDomain, followPort = \
|
|
|
|
getDomainFromActor(followHandle)
|
|
|
|
if followNickname and followDomain:
|
2021-03-11 10:47:52 +00:00
|
|
|
sayStr = 'Stop following ' + \
|
|
|
|
followNickname + '@' + followDomain
|
|
|
|
_sayCommand(sayStr, sayStr,
|
2021-03-10 13:38:11 +00:00
|
|
|
screenreader, systemLanguage, espeak)
|
2021-03-10 16:56:27 +00:00
|
|
|
sessionUnfollow = createSession(proxyType)
|
|
|
|
sendUnfollowRequestViaServer(baseDir, sessionUnfollow,
|
2021-03-10 13:38:11 +00:00
|
|
|
nickname, password,
|
|
|
|
domain, port,
|
|
|
|
followNickname,
|
|
|
|
followDomain,
|
|
|
|
followPort,
|
|
|
|
httpPrefix,
|
|
|
|
cachedWebfingers,
|
|
|
|
personCache,
|
|
|
|
debug, __version__)
|
|
|
|
else:
|
2021-03-11 10:47:52 +00:00
|
|
|
sayStr = followHandle + ' is not valid'
|
|
|
|
_sayCommand(sayStr, sayStr,
|
2021-03-10 13:38:11 +00:00
|
|
|
screenreader, systemLanguage, espeak)
|
|
|
|
print('')
|
2021-03-17 10:04:49 +00:00
|
|
|
elif (commandStr == 'repeat' or commandStr == 'replay' or
|
|
|
|
commandStr == 'rp' or commandStr == 'again' or
|
|
|
|
commandStr == 'say again'):
|
2021-03-11 11:09:33 +00:00
|
|
|
if screenreader and nameStr and \
|
|
|
|
gender and messageStr and content:
|
|
|
|
sayStr = 'Repeating ' + nameStr
|
2021-03-11 11:12:01 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader,
|
2021-03-10 12:37:44 +00:00
|
|
|
systemLanguage, espeak,
|
|
|
|
nameStr, gender)
|
|
|
|
time.sleep(2)
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(content, messageStr, screenreader,
|
2021-03-10 12:40:17 +00:00
|
|
|
systemLanguage, espeak,
|
|
|
|
nameStr, gender)
|
|
|
|
print('')
|
2021-03-17 10:04:49 +00:00
|
|
|
elif (commandStr == 'sounds on' or
|
|
|
|
commandStr == 'sound on' or
|
|
|
|
commandStr == 'sound'):
|
2021-03-10 10:34:06 +00:00
|
|
|
sayStr = 'Notification sounds on'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader,
|
2021-03-10 10:32:08 +00:00
|
|
|
systemLanguage, espeak)
|
2021-03-10 09:57:19 +00:00
|
|
|
notificationSounds = True
|
2021-03-17 10:04:49 +00:00
|
|
|
elif (commandStr == 'sounds off' or
|
|
|
|
commandStr == 'sound off' or
|
|
|
|
commandStr == 'nosound'):
|
2021-03-10 10:34:06 +00:00
|
|
|
sayStr = 'Notification sounds off'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader,
|
2021-03-10 10:32:08 +00:00
|
|
|
systemLanguage, espeak)
|
2021-03-10 09:57:19 +00:00
|
|
|
notificationSounds = False
|
2021-03-17 10:04:49 +00:00
|
|
|
elif (commandStr == 'speak' or
|
|
|
|
commandStr == 'screen reader on' or
|
|
|
|
commandStr == 'speaker on' or
|
|
|
|
commandStr == 'talker on' or
|
|
|
|
commandStr == 'reader on'):
|
2021-03-10 10:25:41 +00:00
|
|
|
if originalScreenReader:
|
|
|
|
screenreader = originalScreenReader
|
2021-03-10 10:34:06 +00:00
|
|
|
sayStr = 'Screen reader on'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, screenreader,
|
2021-03-10 10:25:41 +00:00
|
|
|
systemLanguage, espeak)
|
|
|
|
else:
|
|
|
|
print('No --screenreader option was specified')
|
2021-03-17 10:04:49 +00:00
|
|
|
elif (commandStr == 'mute' or
|
|
|
|
commandStr == 'screen reader off' or
|
|
|
|
commandStr == 'speaker off' or
|
|
|
|
commandStr == 'talker off' or
|
|
|
|
commandStr == 'reader off'):
|
2021-03-10 10:25:41 +00:00
|
|
|
if originalScreenReader:
|
|
|
|
screenreader = None
|
2021-03-10 10:34:06 +00:00
|
|
|
sayStr = 'Screen reader off'
|
2021-03-11 10:47:52 +00:00
|
|
|
_sayCommand(sayStr, sayStr, originalScreenReader,
|
2021-03-10 10:25:41 +00:00
|
|
|
systemLanguage, espeak)
|
|
|
|
else:
|
|
|
|
print('No --screenreader option was specified')
|
2021-03-17 10:04:49 +00:00
|
|
|
elif commandStr.startswith('open'):
|
2021-03-15 17:00:23 +00:00
|
|
|
currIndex = 0
|
2021-03-17 10:04:49 +00:00
|
|
|
if ' ' in commandStr:
|
|
|
|
postIndex = commandStr.split(' ')[-1].strip()
|
2021-03-15 17:00:23 +00:00
|
|
|
if postIndex.isdigit():
|
|
|
|
currIndex = int(postIndex)
|
2021-03-18 19:04:58 +00:00
|
|
|
if currIndex > 0 and boxJson:
|
|
|
|
postJsonObject = \
|
|
|
|
_desktopGetBoxPostObject(boxJson, currIndex)
|
2021-03-19 13:54:30 +00:00
|
|
|
if postJsonObject:
|
|
|
|
if postJsonObject['type'] == 'Announce':
|
|
|
|
recentPostsCache = {}
|
|
|
|
allowLocalNetworkAccess = False
|
|
|
|
YTReplacementDomain = None
|
|
|
|
postJsonObject2 = \
|
|
|
|
downloadAnnounce(session, baseDir,
|
|
|
|
httpPrefix,
|
|
|
|
nickname, domain,
|
|
|
|
postJsonObject,
|
|
|
|
__version__, translate,
|
|
|
|
YTReplacementDomain,
|
|
|
|
allowLocalNetworkAccess,
|
|
|
|
recentPostsCache, False)
|
|
|
|
if postJsonObject2:
|
|
|
|
postJsonObject = postJsonObject2
|
2021-03-18 19:04:58 +00:00
|
|
|
if postJsonObject:
|
|
|
|
content = postJsonObject['object']['content']
|
|
|
|
messageStr, detectedLinks = \
|
|
|
|
speakableText(baseDir, content, translate)
|
|
|
|
linkOpened = False
|
|
|
|
for url in detectedLinks:
|
|
|
|
if '://' in url:
|
|
|
|
webbrowser.open(url)
|
|
|
|
linkOpened = True
|
|
|
|
if linkOpened:
|
|
|
|
sayStr = 'Opened web links'
|
|
|
|
_sayCommand(sayStr, sayStr, originalScreenReader,
|
|
|
|
systemLanguage, espeak)
|
|
|
|
else:
|
|
|
|
sayStr = 'There are no web links to open.'
|
|
|
|
_sayCommand(sayStr, sayStr, originalScreenReader,
|
|
|
|
systemLanguage, espeak)
|
2021-03-15 14:53:21 +00:00
|
|
|
print('')
|
2021-03-17 10:04:49 +00:00
|
|
|
elif commandStr.startswith('h'):
|
2021-03-16 23:10:14 +00:00
|
|
|
_desktopHelp()
|