Rename toot to witchie

This commit is contained in:
Ngô Ngọc Đức Huy 2023-12-12 10:24:20 +07:00
parent 1c5abb8419
commit 41fff21b8d
Signed by: xarvos
GPG key ID: 904AF1C7CDF695C3
36 changed files with 157 additions and 166 deletions

View file

@ -10,7 +10,7 @@ publish :
test: test:
pytest -v pytest -v
flake8 flake8
vermin toot vermin witchie
coverage: coverage:
coverage erase coverage erase
@ -20,7 +20,7 @@ coverage:
clean : clean :
find . -name "*pyc" | xargs rm -rf $1 find . -name "*pyc" | xargs rm -rf $1
rm -rf build dist MANIFEST htmlcov toot*.tar.gz rm -rf build dist MANIFEST htmlcov witchie*.tar.gz
changelog: changelog:
./scripts/generate_changelog > CHANGELOG.md ./scripts/generate_changelog > CHANGELOG.md
@ -33,4 +33,4 @@ docs-serve:
mdbook serve mdbook serve
docs-deploy: docs docs-deploy: docs
rsync --archive --compress --delete --stats book/ bezdomni:web/toot rsync --archive --compress --delete --stats book/ bezdomni:web/witchie

View file

@ -1,27 +1,16 @@
============================ ============================
Toot - a Mastodon CLI client Witchie - an Akkoma CLI client
============================ ============================
.. image:: https://raw.githubusercontent.com/ihabunek/toot/master/trumpet.png Witchie is a CLI and TUI tool for interacting with Akkoma instances from the command line.
It is a fork of [ibuhanek's toot](https://github.com/ihabunek/toot) for Mastodon.
Toot is a CLI and TUI tool for interacting with Mastodon instances from the command line.
.. image:: https://img.shields.io/badge/author-%40ihabunek-blue.svg?maxAge=3600&style=flat-square
:target: https://mastodon.social/@ihabunek
.. image:: https://img.shields.io/github/license/ihabunek/toot.svg?maxAge=3600&style=flat-square
:target: https://opensource.org/licenses/GPL-3.0
.. image:: https://img.shields.io/pypi/v/toot.svg?maxAge=3600&style=flat-square
:target: https://pypi.python.org/pypi/toot
Resources Resources
--------- ---------
* Homepage: https://github.com/ihabunek/toot * Homepage: https://sr.ht/~huyngo/witchie
* Issues: https://github.com/ihabunek/toot/issues * Mailing list for discussion: https://lists.sr.ht/~huyngo/witchie
* Documentation: https://toot.bezdomni.net/ * Bugs/feature requests: https://todo.sr.ht/~huyngo/witchie
* Mailing list for discussion, support and patches:
https://lists.sr.ht/~ihabunek/toot-discuss
* Informal discussion: #toot IRC channel on `libera.chat <https://libera.chat/>`_
Features Features
-------- --------
@ -30,12 +19,12 @@ Features
* Support for media uploads, spoiler text, sensitive content * Support for media uploads, spoiler text, sensitive content
* Search by account or hash tag * Search by account or hash tag
* Following, muting and blocking accounts * Following, muting and blocking accounts
* Simple switching between authenticated in Mastodon accounts * Simple switching between authenticated accounts
Terminal User Interface Terminal User Interface
----------------------- -----------------------
toot includes a terminal user interface (TUI). Run it with ``toot tui``. witchie includes a terminal user interface (TUI). Run it with ``witchie tui``.
.. image :: https://raw.githubusercontent.com/ihabunek/toot/master/docs/images/tui_list.png .. image :: https://raw.githubusercontent.com/ihabunek/toot/master/docs/images/tui_list.png
@ -45,6 +34,7 @@ toot includes a terminal user interface (TUI). Run it with ``toot tui``.
License License
------- -------
Copyright Ivan Habunek <ivan@habunek.com> and contributors. Copyright 2017-2023 Ivan Habunek <ivan@habunek.com> and contributors.
Copyright 2023 Ngô Ngọc Đức Huy <huyngo@disroot.org>
Licensed under `GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>`_, see `LICENSE <LICENSE>`_. Licensed under `GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>`_, see `LICENSE <LICENSE>`_.

View file

@ -3,7 +3,7 @@
from setuptools import setup from setuptools import setup
long_description = """ long_description = """
Toot is a CLI and TUI tool for interacting with Mastodon instances from the Witchie is a CLI and TUI tool for interacting with Akkoma instances from the
command line. command line.
Allows posting text and media to the timeline, searching, following, muting Allows posting text and media to the timeline, searching, following, muting
@ -11,18 +11,18 @@ and blocking accounts and other actions.
""" """
setup( setup(
name='toot', name='witchie',
version='0.39.0', version='0.39.0',
description='Mastodon CLI client', description='Akkoma CLI client',
long_description=long_description.strip(), long_description=long_description.strip(),
author='Ivan Habunek', author='Ngô Ngọc Đức Huy',
author_email='ivan@habunek.com', author_email='huyngo@disroot.org',
url='https://github.com/ihabunek/toot/', url='https://git.sr.ht/~huyngo/witchie/',
project_urls={ project_urls={
'Documentation': 'https://toot.bezdomni.net/', 'Documentation': 'https://toot.bezdomni.net/',
'Issue tracker': 'https://github.com/ihabunek/toot/issues/', 'Issue tracker': 'https://todo.sr.ht/~huyngo/witchie/',
}, },
keywords='mastodon toot', keywords='akkoma',
license='GPLv3', license='GPLv3',
classifiers=[ classifiers=[
'Development Status :: 4 - Beta', 'Development Status :: 4 - Beta',
@ -31,7 +31,7 @@ setup(
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
], ],
packages=['toot', 'toot.tui', 'toot.tui.richtext', 'toot.utils'], packages=['akkoma', 'akkoma.tui', 'akkoma.tui.richtext', 'akkoma.utils'],
python_requires=">=3.7", python_requires=">=3.7",
install_requires=[ install_requires=[
"requests>=2.13,<3.0", "requests>=2.13,<3.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -11,24 +11,24 @@ User = namedtuple('User', ['instance', 'username', 'access_token'])
DEFAULT_INSTANCE = 'https://mastodon.social' DEFAULT_INSTANCE = 'https://mastodon.social'
CLIENT_NAME = 'toot - a Mastodon CLI client' CLIENT_NAME = 'witchie - an Akkoma CLI client'
CLIENT_WEBSITE = 'https://github.com/ihabunek/toot' CLIENT_WEBSITE = 'https://sr.ht/~huyngo/witchie'
TOOT_CONFIG_DIR_NAME = "toot" CONFIG_DIR_NAME = "witchie"
def get_config_dir(): def get_config_dir():
"""Returns the path to toot config directory""" """Returns the path to witchie config directory"""
# On Windows, store the config in roaming appdata # On Windows, store the config in roaming appdata
if sys.platform == "win32" and "APPDATA" in os.environ: if sys.platform == "win32" and "APPDATA" in os.environ:
return join(os.getenv("APPDATA"), TOOT_CONFIG_DIR_NAME) return join(os.getenv("APPDATA"), CONFIG_DIR_NAME)
# Respect XDG_CONFIG_HOME env variable if set # Respect XDG_CONFIG_HOME env variable if set
# https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html # https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
if "XDG_CONFIG_HOME" in os.environ: if "XDG_CONFIG_HOME" in os.environ:
config_home = expanduser(os.environ["XDG_CONFIG_HOME"]) config_home = expanduser(os.environ["XDG_CONFIG_HOME"])
return join(config_home, TOOT_CONFIG_DIR_NAME) return join(config_home, CONFIG_DIR_NAME)
# Default to ~/.config/toot/ # Default to ~/.config/witchie/
return join(expanduser("~"), ".config", TOOT_CONFIG_DIR_NAME) return join(expanduser("~"), ".config", CONFIG_DIR_NAME)

View file

@ -7,9 +7,9 @@ from requests import Response
from typing import BinaryIO, List, Optional from typing import BinaryIO, List, Optional
from urllib.parse import urlparse, urlencode, quote from urllib.parse import urlparse, urlencode, quote
from toot import App, User, http, CLIENT_NAME, CLIENT_WEBSITE from witchie import App, User, http, CLIENT_NAME, CLIENT_WEBSITE
from toot.exceptions import AuthenticationError, ConsoleError from witchie.exceptions import AuthenticationError, ConsoleError
from toot.utils import drop_empty_values, str_bool, str_bool_nullable from witchie.utils import drop_empty_values, str_bool, str_bool_nullable
SCOPES = 'read write follow' SCOPES = 'read write follow'
@ -255,7 +255,7 @@ def scheduled_statuses(app, user):
def delete_status(app, user, status_id): def delete_status(app, user, status_id):
""" """
Deletes a status with given ID. Deletes a status with given ID.
https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#deleting-a-status https://docs.joinmastodon.org/methods/statuses/#delete
""" """
return http.delete(app, user, f"/api/v1/statuses/{status_id}") return http.delete(app, user, f"/api/v1/statuses/{status_id}")

View file

@ -4,9 +4,9 @@ import webbrowser
from builtins import input from builtins import input
from getpass import getpass from getpass import getpass
from toot import api, config, DEFAULT_INSTANCE, User, App from witchie import api, config, DEFAULT_INSTANCE, User, App
from toot.exceptions import ApiError, ConsoleError from witchie.exceptions import ApiError, ConsoleError
from toot.output import print_out from witchie.output import print_out
from urllib.parse import urlparse from urllib.parse import urlparse
@ -104,8 +104,8 @@ def login_interactive(app, email=None):
BROWSER_LOGIN_EXPLANATION = """ BROWSER_LOGIN_EXPLANATION = """
This authentication method requires you to log into your Mastodon instance This authentication method requires you to log into your Akkoma instance
in your browser, where you will be asked to authorize <yellow>toot</yellow> to access in your browser, where you will be asked to authorize <yellow>witchie</yellow> to access
your account. When you do, you will be given an <yellow>authorization code</yellow> your account. When you do, you will be given an <yellow>authorization code</yellow>
which you need to paste here. which you need to paste here.
""" """

View file

@ -6,15 +6,15 @@ import platform
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from time import sleep, time from time import sleep, time
from toot import api, config, __version__ from witchie import api, config, __version__
from toot.auth import login_interactive, login_browser_interactive, create_app_interactive from witchie.auth import login_interactive, login_browser_interactive, create_app_interactive
from toot.entities import Account, Instance, Notification, Status, from_dict from witchie.entities import Account, Instance, Notification, Status, from_dict
from toot.exceptions import ApiError, ConsoleError from witchie.exceptions import ApiError, ConsoleError
from toot.output import (print_lists, print_out, print_instance, print_account, print_acct_list, from witchie.output import (print_lists, print_out, print_instance, print_account, print_acct_list,
print_search_results, print_status, print_table, print_timeline, print_notifications, print_search_results, print_status, print_table, print_timeline, print_notifications,
print_tag_list, print_list_accounts, print_user_list) print_tag_list, print_list_accounts, print_user_list)
from toot.utils import args_get_instance, delete_tmp_status_file, editor_input, multiline_input, EOF_KEY from witchie.utils import args_get_instance, delete_tmp_status_file, editor_input, multiline_input, EOF_KEY
from toot.utils.datetime import parse_datetime from witchie.utils.datetime import parse_datetime
def get_timeline_generator(app, user, args): def get_timeline_generator(app, user, args):
@ -85,10 +85,10 @@ def thread(app, user, args):
if args.json: if args.json:
print(context_response.text) print(context_response.text)
else: else:
toot = api.fetch_status(app, user, args.status_id).json() post = api.fetch_status(app, user, args.status_id).json()
context = context_response.json() context = context_response.json()
statuses = chain(context["ancestors"], [toot], context["descendants"]) statuses = chain(context["ancestors"], [post], context["descendants"])
print_timeline(from_dict(Status, s) for s in statuses) print_timeline(from_dict(Status, s) for s in statuses)
@ -129,9 +129,9 @@ def post(app, user, args):
if "scheduled_at" in status: if "scheduled_at" in status:
scheduled_at = parse_datetime(status["scheduled_at"]) scheduled_at = parse_datetime(status["scheduled_at"])
scheduled_at = datetime.strftime(scheduled_at, "%Y-%m-%d %H:%M:%S%z") scheduled_at = datetime.strftime(scheduled_at, "%Y-%m-%d %H:%M:%S%z")
print_out(f"Toot scheduled for: <green>{scheduled_at}</green>") print_out(f"Post scheduled for: <green>{scheduled_at}</green>")
else: else:
print_out(f"Toot posted: <green>{status['url']}") print_out(f"Post posted: <green>{status['url']}")
delete_tmp_status_file() delete_tmp_status_file()
@ -146,7 +146,7 @@ def _get_status_text(text, editor, media):
if editor: if editor:
text = editor_input(editor, text) text = editor_input(editor, text)
elif not text and not media: elif not text and not media:
print_out("Write or paste your toot. Press <yellow>{}</yellow> to post it.".format(EOF_KEY)) print_out("Write or paste your post. Press <yellow>{}</yellow> to post it.".format(EOF_KEY))
text = multiline_input() text = multiline_input()
return text return text
@ -318,7 +318,7 @@ def auth(app, user, args):
def env(app, user, args): def env(app, user, args):
print_out(f"toot {__version__}") print_out(f"witchie {__version__}")
print_out(f"Python {sys.version}") print_out(f"Python {sys.version}")
print_out(platform.platform()) print_out(platform.platform())

View file

@ -4,17 +4,17 @@ import os
from functools import wraps from functools import wraps
from os.path import dirname, join from os.path import dirname, join
from toot import User, App, get_config_dir from witchie import User, App, get_config_dir
from toot.exceptions import ConsoleError from witchie.exceptions import ConsoleError
from toot.output import print_out from witchie.output import print_out
TOOT_CONFIG_FILE_NAME = "config.json" WITCHIE_CONFIG_FILE_NAME = "config.json"
def get_config_file_path(): def get_config_file_path():
"""Returns the path to toot config file.""" """Returns the path to witchie config file."""
return join(get_config_dir(), TOOT_CONFIG_FILE_NAME) return join(get_config_dir(), WITCHIE_CONFIG_FILE_NAME)
def user_id(user): def user_id(user):
@ -22,7 +22,7 @@ def user_id(user):
def make_config(path): def make_config(path):
"""Creates an empty toot configuration file.""" """Creates an empty witchie configuration file."""
config = { config = {
"apps": {}, "apps": {},
"users": {}, "users": {},

View file

@ -7,10 +7,10 @@ import sys
from argparse import ArgumentParser, FileType, ArgumentTypeError, Action from argparse import ArgumentParser, FileType, ArgumentTypeError, Action
from collections import namedtuple from collections import namedtuple
from itertools import chain from itertools import chain
from toot import config, commands, CLIENT_NAME, CLIENT_WEBSITE, __version__, settings from witchie import config, commands, CLIENT_NAME, CLIENT_WEBSITE, __version__, settings
from toot.exceptions import ApiError, ConsoleError from witchie.exceptions import ApiError, ConsoleError
from toot.output import print_out, print_err from witchie.output import print_out, print_err
from toot.settings import get_setting from witchie.settings import get_setting
VISIBILITY_CHOICES = ["public", "unlisted", "private", "direct"] VISIBILITY_CHOICES = ["public", "unlisted", "private", "direct"]
VISIBILITY_CHOICES_STR = ", ".join(f"'{v}'" for v in VISIBILITY_CHOICES) VISIBILITY_CHOICES_STR = ", ".join(f"'{v}'" for v in VISIBILITY_CHOICES)
@ -62,7 +62,7 @@ class BooleanOptionalAction(Action):
def get_default_visibility(): def get_default_visibility():
return os.getenv("TOOT_POST_VISIBILITY", "public") return os.getenv("WITCHIE_POST_VISIBILITY", "public")
def language(value): def language(value):
@ -94,8 +94,8 @@ def privacy(value):
def timeline_count(value): def timeline_count(value):
n = int(value) n = int(value)
if not 0 < n <= 20: if not 0 < n <= 40:
raise ArgumentTypeError("Number of toots should be between 1 and 20.") raise ArgumentTypeError("Number of posts should be between 1 and 40.")
return n return n
@ -225,7 +225,7 @@ visibility_arg = (["-v", "--visibility"], {
"default": get_default_visibility(), "default": get_default_visibility(),
"help": f"Post visibility. One of: {VISIBILITY_CHOICES_STR}. Defaults to " "help": f"Post visibility. One of: {VISIBILITY_CHOICES_STR}. Defaults to "
f"'{get_default_visibility()}' which can be overridden by setting " f"'{get_default_visibility()}' which can be overridden by setting "
"the TOOT_POST_VISIBILITY environment variable", "the WITCHIE_POST_VISIBILITY environment variable",
}) })
tag_arg = (["tag_name"], { tag_arg = (["tag_name"], {
@ -239,7 +239,7 @@ json_arg = (["--json"], {
"help": "print json instead of plaintext", "help": "print json instead of plaintext",
}) })
# Arguments for selecting a timeline (see `toot.commands.get_timeline_generator`) # Arguments for selecting a timeline (see `witchie.commands.get_timeline_generator`)
common_timeline_args = [ common_timeline_args = [
(["-p", "--public"], { (["-p", "--public"], {
"action": "store_true", "action": "store_true",
@ -272,7 +272,7 @@ common_timeline_args = [
timeline_and_bookmark_args = [ timeline_and_bookmark_args = [
(["-c", "--count"], { (["-c", "--count"], {
"type": timeline_count, "type": timeline_count,
"help": "number of toots to show per page (1-20, default 10).", "help": "number of posts to show per page (1-40, default 10).",
"default": 10, "default": 10,
}), }),
(["-r", "--reverse"], { (["-r", "--reverse"], {
@ -283,7 +283,7 @@ timeline_and_bookmark_args = [
(["-1", "--once"], { (["-1", "--once"], {
"action": "store_true", "action": "store_true",
"default": False, "default": False,
"help": "Only show the first <count> toots, do not prompt to continue.", "help": "Only show the first <count> posts, do not prompt to continue.",
}), }),
] ]
@ -379,7 +379,7 @@ AUTH_COMMANDS = [
TUI_COMMANDS = [ TUI_COMMANDS = [
Command( Command(
name="tui", name="tui",
description="Launches the toot terminal user interface", description="Launches the post terminal user interface",
arguments=[ arguments=[
(["--relative-datetimes"], { (["--relative-datetimes"], {
"action": "store_true", "action": "store_true",
@ -463,10 +463,10 @@ READ_COMMANDS = [
), ),
Command( Command(
name="thread", name="thread",
description="Show toot thread items", description="Show post thread items",
arguments=[ arguments=[
(["status_id"], { (["status_id"], {
"help": "Show thread for toot.", "help": "Show thread for post.",
}), }),
json_arg, json_arg,
], ],
@ -540,13 +540,13 @@ POST_COMMANDS = [
}), }),
(["-l", "--language"], { (["-l", "--language"], {
"type": language, "type": language,
"help": "ISO 639-1 language code of the toot, to skip automatic detection", "help": "ISO 639-1 language code of the post, to skip automatic detection",
}), }),
(["-e", "--editor"], { (["-e", "--editor"], {
"type": editor, "type": editor,
"nargs": "?", "nargs": "?",
"const": os.getenv("EDITOR", ""), # option given without value "const": os.getenv("EDITOR", ""), # option given without value
"help": "Specify an editor to compose your toot, " "help": "Specify an editor to compose your post, "
"defaults to editor defined in $EDITOR env variable.", "defaults to editor defined in $EDITOR env variable.",
}), }),
(["--scheduled-at"], { (["--scheduled-at"], {
@ -556,7 +556,7 @@ POST_COMMANDS = [
}), }),
(["--scheduled-in"], { (["--scheduled-in"], {
"type": duration, "type": duration,
"help": f"""Schedule the toot to be posted after a given amount "help": f"""Schedule the post to be posted after a given amount
of time, {DURATION_EXAMPLES}. Must be at least 5 of time, {DURATION_EXAMPLES}. Must be at least 5
minutes.""", minutes.""",
}), }),
@ -876,18 +876,18 @@ def print_usage():
for cmd in cmds: for cmd in cmds:
cmd_name = cmd.name.ljust(max_name_len + 2) cmd_name = cmd.name.ljust(max_name_len + 2)
print_out(" <yellow>toot {}</yellow> {}".format(cmd_name, cmd.description)) print_out(" <yellow>witchie {}</yellow> {}".format(cmd_name, cmd.description))
print_out("") print_out("")
print_out("To get help for each command run:") print_out("To get help for each command run:")
print_out(" <yellow>toot \\<command> --help</yellow>") print_out(" <yellow>witchie \\<command> --help</yellow>")
print_out("") print_out("")
print_out("<green>{}</green>".format(CLIENT_WEBSITE)) print_out("<green>{}</green>".format(CLIENT_WEBSITE))
def get_argument_parser(name, command): def get_argument_parser(name, command):
parser = ArgumentParser( parser = ArgumentParser(
prog='toot %s' % name, prog='witchie %s' % name,
description=command.description, description=command.description,
epilog=CLIENT_WEBSITE) epilog=CLIENT_WEBSITE)
@ -918,7 +918,7 @@ def run_command(app, user, name, args):
if not command: if not command:
print_err(f"Unknown command '{name}'") print_err(f"Unknown command '{name}'")
print_out("Run <yellow>toot --help</yellow> to show a list of available commands.") print_out("Run <yellow>witchie --help</yellow> to show a list of available commands.")
return return
parser = get_argument_parser(name, command) parser = get_argument_parser(name, command)
@ -932,7 +932,7 @@ def run_command(app, user, name, args):
if command.require_auth and (not user or not app): if command.require_auth and (not user or not app):
print_err("This command requires that you are logged in.") print_err("This command requires that you are logged in.")
print_err("Please run `toot login` first.") print_err("Please run `witchie login` first.")
return return
fn = commands.__dict__.get(name) fn = commands.__dict__.get(name)

View file

@ -1,7 +1,7 @@
""" """
Dataclasses which represent entities returned by the Mastodon API. Dataclasses which represent entities returned by the Mastodon API.
Data classes my have an optional static method named `__toot_prepare__` which is Data classes my have an optional static method named `__post_prepare__` which is
used when constructing the data class using `from_dict`. The method will be used when constructing the data class using `from_dict`. The method will be
called with the dict and may modify it and return a modified dict. This is used called with the dict and may modify it and return a modified dict. This is used
to implement any pre-processing which may be required, e.g. to support to implement any pre-processing which may be required, e.g. to support
@ -16,9 +16,9 @@ from functools import lru_cache
from typing import Any, Dict, List, Optional, Tuple, Type, TypeVar, Union from typing import Any, Dict, List, Optional, Tuple, Type, TypeVar, Union
from typing import get_type_hints from typing import get_type_hints
from toot.typing_compat import get_args, get_origin from witchie.typing_compat import get_args, get_origin
from toot.utils import get_text from witchie.utils import get_text
from toot.utils.datetime import parse_datetime from witchie.utils.datetime import parse_datetime
@dataclass @dataclass
@ -76,7 +76,7 @@ class Account:
source: Optional[dict] source: Optional[dict]
@staticmethod @staticmethod
def __toot_prepare__(obj: Dict) -> Dict: def __post_prepare__(obj: Dict) -> Dict:
# Pleroma has not yet converted last_status_at from datetime to date # Pleroma has not yet converted last_status_at from datetime to date
# so trim it here so it doesn't break when converting to date. # so trim it here so it doesn't break when converting to date.
# See: https://git.pleroma.social/pleroma/pleroma/-/issues/1470 # See: https://git.pleroma.social/pleroma/pleroma/-/issues/1470
@ -266,7 +266,8 @@ class Status:
return self.reblog or self return self.reblog or self
@staticmethod @staticmethod
def __toot_prepare__(obj: Dict) -> Dict: def __post_prepare__(obj: Dict) -> Dict:
# TODO: check this
# Pleroma has a bug where created_at is set to an empty string. # Pleroma has a bug where created_at is set to an empty string.
# To avoid marking created_at as optional, which would require work # To avoid marking created_at as optional, which would require work
# because we count on it always existing, set it to current datetime. # because we count on it always existing, set it to current datetime.
@ -430,8 +431,8 @@ class ConversionError(Exception):
def from_dict(cls: Type[T], data: Dict) -> T: def from_dict(cls: Type[T], data: Dict) -> T:
"""Convert a nested dict into an instance of `cls`.""" """Convert a nested dict into an instance of `cls`."""
# Apply __toot_prepare__ if it exists # Apply __post_prepare__ if it exists
prepare = getattr(cls, '__toot_prepare__', None) prepare = getattr(cls, '__post_prepare__', None)
if prepare: if prepare:
data = prepare(data) data = prepare(data)

View file

@ -1,15 +1,15 @@
from requests import Request, Session from requests import Request, Session
from requests.exceptions import RequestException from requests.exceptions import RequestException
from toot import __version__ from witchie import __version__
from toot.exceptions import NotFoundError, ApiError from witchie.exceptions import NotFoundError, ApiError
from toot.logging import log_request, log_request_exception, log_response from witchie.logging import log_request, log_request_exception, log_response
def send_request(request, allow_redirects=True): def send_request(request, allow_redirects=True):
# Set a user agent string # Set a user agent string
# Required for accessing instances using Cloudfront DDOS protection. # Required for accessing instances using Cloudfront DDOS protection.
request.headers["User-Agent"] = "toot/{}".format(__version__) request.headers["User-Agent"] = "witchie/{}".format(__version__)
log_request(request) log_request(request)

View file

@ -5,7 +5,7 @@ from logging import getLogger
from requests import Request, RequestException, Response from requests import Request, RequestException, Response
from urllib.parse import urlencode from urllib.parse import urlencode
logger = getLogger("toot") logger = getLogger("witchie")
VERBOSE = "--verbose" in sys.argv VERBOSE = "--verbose" in sys.argv

View file

@ -4,10 +4,10 @@ import sys
import textwrap import textwrap
from functools import lru_cache from functools import lru_cache
from toot import settings from witchie import settings
from toot.utils import get_text, html_to_paragraphs from witchie.utils import get_text, html_to_paragraphs
from toot.entities import Account, Instance, Notification, Poll, Status from witchie.entities import Account, Instance, Notification, Poll, Status
from toot.wcstring import wc_wrap from witchie.wcstring import wc_wrap
from typing import Iterable, List from typing import Iterable, List
from wcwidth import wcswidth from wcwidth import wcswidth

View file

@ -4,17 +4,17 @@ import sys
from functools import lru_cache from functools import lru_cache
from os.path import exists, join from os.path import exists, join
from tomlkit import parse from tomlkit import parse
from toot import get_config_dir from witchie import get_config_dir
from typing import Optional, Type, TypeVar from typing import Optional, Type, TypeVar
DISABLE_SETTINGS = False DISABLE_SETTINGS = False
TOOT_SETTINGS_FILE_NAME = "settings.toml" WITCHIE_SETTINGS_FILE_NAME = "settings.toml"
def get_settings_path(): def get_settings_path():
return join(get_config_dir(), TOOT_SETTINGS_FILE_NAME) return join(get_config_dir(), WITCHIE_SETTINGS_FILE_NAME)
def load_settings() -> dict: def load_settings() -> dict:
@ -72,7 +72,7 @@ def get_debug() -> bool:
def get_debug_file() -> Optional[str]: def get_debug_file() -> Optional[str]:
from_env = os.getenv("TOOT_LOG_FILE") from_env = os.getenv("WITCHIE_LOG_FILE")
if from_env: if from_env:
return from_env return from_env

View file

@ -4,9 +4,9 @@ import urwid
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from toot import api, config, __version__, settings from witchie import api, config, __version__, settings
from toot.console import get_default_visibility from witchie.console import get_default_visibility
from toot.exceptions import ApiError from witchie.exceptions import ApiError
from .compose import StatusComposer from .compose import StatusComposer
from .constants import PALETTE from .constants import PALETTE
@ -15,14 +15,14 @@ from .overlays import ExceptionStackTrace, GotoMenu, Help, StatusSource, StatusL
from .overlays import StatusDeleteConfirmation, Account from .overlays import StatusDeleteConfirmation, Account
from .poll import Poll from .poll import Poll
from .timeline import Timeline from .timeline import Timeline
from .utils import get_max_toot_chars, parse_content_links, copy_to_clipboard from .utils import get_max_post_chars, parse_content_links, copy_to_clipboard
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
urwid.set_encoding('UTF-8') urwid.set_encoding('UTF-8')
DEFAULT_MAX_TOOT_CHARS = 500 DEFAULT_MAX_POST_CHARS = 500
class Header(urwid.WidgetWrap): class Header(urwid.WidgetWrap):
@ -32,7 +32,7 @@ class Header(urwid.WidgetWrap):
self.text = urwid.Text("") self.text = urwid.Text("")
self.cols = urwid.Columns([ self.cols = urwid.Columns([
("pack", urwid.Text(('header_bold', 'toot'))), ("pack", urwid.Text(('header_bold', 'post'))),
("pack", urwid.Text(('header', ' | {}@{}'.format(user.username, app.instance)))), ("pack", urwid.Text(('header', ' | {}@{}'.format(user.username, app.instance)))),
("pack", self.text), ("pack", self.text),
]) ])
@ -124,14 +124,14 @@ class TUI(urwid.Frame):
self.executor = ThreadPoolExecutor(max_workers=1) self.executor = ThreadPoolExecutor(max_workers=1)
self.timeline_generator = api.home_timeline_generator(app, user, limit=40) self.timeline_generator = api.home_timeline_generator(app, user, limit=40)
# Show intro screen while toots are being loaded # Show intro screen while posts are being loaded
self.body = self.build_intro() self.body = self.build_intro()
self.header = Header(app, user) self.header = Header(app, user)
self.footer = Footer() self.footer = Footer()
self.footer.set_status("Loading...") self.footer.set_status("Loading...")
# Default max status length, updated on startup # Default max status length, updated on startup
self.max_toot_chars = DEFAULT_MAX_TOOT_CHARS self.max_post_chars = DEFAULT_MAX_POST_CHARS
self.timeline = None self.timeline = None
self.overlay = None self.overlay = None
@ -157,7 +157,7 @@ class TUI(urwid.Frame):
# NB: Padding with width="clip" will convert the fixed BigText widget # NB: Padding with width="clip" will convert the fixed BigText widget
# to a flow widget so it can be used in a Pile. # to a flow widget so it can be used in a Pile.
big_text = "Toot {}".format(__version__) big_text = "Witchie {}".format(__version__)
big_text = urwid.BigText(("intro_bigtext", big_text), font) big_text = urwid.BigText(("intro_bigtext", big_text), font)
big_text = urwid.Padding(big_text, align="center", width="clip") big_text = urwid.Padding(big_text, align="center", width="clip")
@ -170,7 +170,7 @@ class TUI(urwid.Frame):
" and contributors" " and contributors"
], align="center"), ], align="center"),
urwid.Divider(), urwid.Divider(),
urwid.Text(("intro_smalltext", "Loading toots..."), align="center"), urwid.Text(("intro_smalltext", "Loading posts..."), align="center"),
]) ])
return urwid.Filler(intro) return urwid.Filler(intro)
@ -296,7 +296,7 @@ class TUI(urwid.Frame):
def async_load_instance(self): def async_load_instance(self):
""" """
Attempt to update max_toot_chars from instance data. Attempt to update max_post_chars from instance data.
Does not work on vanilla Mastodon, works on Pleroma. Does not work on vanilla Mastodon, works on Pleroma.
See: https://github.com/tootsuite/mastodon/issues/4915 See: https://github.com/tootsuite/mastodon/issues/4915
@ -309,8 +309,8 @@ class TUI(urwid.Frame):
return api.get_instance(self.app.base_url).json() return api.get_instance(self.app.base_url).json()
def _done(instance): def _done(instance):
self.max_toot_chars = get_max_toot_chars(instance, DEFAULT_MAX_TOOT_CHARS) self.max_post_chars = get_max_post_chars(instance, DEFAULT_MAX_POST_CHARS)
logger.info(f"Max toot chars set to: {self.max_toot_chars}") logger.info(f"Max post chars set to: {self.max_post_chars}")
if "translation" in instance: if "translation" in instance:
# instance is advertising translation service # instance is advertising translation service
@ -400,7 +400,7 @@ class TUI(urwid.Frame):
def _post(timeline, *args): def _post(timeline, *args):
self.post_status(*args) self.post_status(*args)
composer = StatusComposer(self.max_toot_chars, self.user.username, in_reply_to) composer = StatusComposer(self.max_post_chars, self.user.username, in_reply_to)
urwid.connect_signal(composer, "close", _close) urwid.connect_signal(composer, "close", _close)
urwid.connect_signal(composer, "post", _post) urwid.connect_signal(composer, "post", _post)
self.open_overlay(composer, title="Compose status") self.open_overlay(composer, title="Compose status")

View file

@ -1,7 +1,7 @@
import urwid import urwid
import logging import logging
from toot.console import get_default_visibility from witchie.console import get_default_visibility
from .constants import VISIBILITY_OPTIONS from .constants import VISIBILITY_OPTIONS
from .widgets import Button, EditBox from .widgets import Button, EditBox

View file

@ -1,6 +1,6 @@
from collections import namedtuple from collections import namedtuple
from toot.utils.datetime import parse_datetime from witchie.utils.datetime import parse_datetime
Author = namedtuple("Author", ["account", "display_name", "username"]) Author = namedtuple("Author", ["account", "display_name", "username"])

View file

@ -3,11 +3,11 @@ import traceback
import urwid import urwid
import webbrowser import webbrowser
from toot import __version__ from witchie import __version__
from toot import api from witchie import api
from toot.tui.utils import highlight_keys from witchie.tui.utils import highlight_keys
from toot.tui.widgets import Button, EditBox, SelectableText from witchie.tui.widgets import Button, EditBox, SelectableText
from toot.tui.richtext import html_to_widgets from witchie.tui.richtext import html_to_widgets
class StatusSource(urwid.Padding): class StatusSource(urwid.Padding):
@ -198,7 +198,7 @@ class Help(urwid.Padding):
def h(text): def h(text):
return highlight_keys(text, "shortcut") return highlight_keys(text, "shortcut")
yield urwid.Text(("bold", "toot {}".format(__version__))) yield urwid.Text(("bold", "witchie {}".format(__version__)))
yield urwid.Divider() yield urwid.Divider()
yield urwid.Text(("bold", "General usage")) yield urwid.Text(("bold", "General usage"))
yield urwid.Divider() yield urwid.Divider()
@ -209,7 +209,7 @@ class Help(urwid.Padding):
yield urwid.Divider() yield urwid.Divider()
yield urwid.Text(("bold", "General keys")) yield urwid.Text(("bold", "General keys"))
yield urwid.Divider() yield urwid.Divider()
yield urwid.Text(h(" [Q] - quit toot")) yield urwid.Text(h(" [Q] - quit witchie"))
yield urwid.Text(h(" [G] - go to - switch timelines")) yield urwid.Text(h(" [G] - go to - switch timelines"))
yield urwid.Text(h(" [E] - save/unsave (pin) current timeline")) yield urwid.Text(h(" [E] - save/unsave (pin) current timeline"))
yield urwid.Text(h(" [,] - refresh current timeline")) yield urwid.Text(h(" [,] - refresh current timeline"))
@ -236,7 +236,7 @@ class Help(urwid.Padding):
yield urwid.Text(("bold", "Links")) yield urwid.Text(("bold", "Links"))
yield urwid.Divider() yield urwid.Divider()
yield link("Documentation: ", "https://toot.bezdomni.net/") yield link("Documentation: ", "https://toot.bezdomni.net/")
yield link("Project home: ", "https://github.com/ihabunek/toot/") yield link("Project home: ", "https://sr.ht/~huyngo/witchie")
class Account(urwid.ListBox): class Account(urwid.ListBox):

View file

@ -1,8 +1,8 @@
import urwid import urwid
from toot import api from witchie import api
from toot.exceptions import ApiError from witchie.exceptions import ApiError
from toot.utils.datetime import parse_datetime from witchie.utils.datetime import parse_datetime
from .widgets import Button, CheckBox, RadioButton from .widgets import Button, CheckBox, RadioButton
from .richtext import html_to_widgets from .richtext import html_to_widgets

View file

@ -1,7 +1,7 @@
import urwid import urwid
from toot.tui.utils import highlight_hashtags from witchie.tui.utils import highlight_hashtags
from toot.utils import format_content from witchie.utils import format_content
from typing import List from typing import List
try: try:

View file

@ -3,8 +3,8 @@ import urwid
import unicodedata import unicodedata
from bs4.element import NavigableString, Tag from bs4.element import NavigableString, Tag
from toot.tui.constants import PALETTE from witchie.tui.constants import PALETTE
from toot.utils import parse_html, urlencode_url from witchie.utils import parse_html, urlencode_url
from typing import List, Tuple from typing import List, Tuple
from urwid.util import decompose_tagmarkup from urwid.util import decompose_tagmarkup
from urwidgets import Hyperlink, TextEmbed from urwidgets import Hyperlink, TextEmbed

View file

@ -4,17 +4,17 @@ import webbrowser
from typing import List, Optional from typing import List, Optional
from toot.tui import app from witchie.tui import app
from toot.tui.richtext import html_to_widgets, url_to_widget from witchie.tui.richtext import html_to_widgets, url_to_widget
from toot.utils.datetime import parse_datetime, time_ago from witchie.utils.datetime import parse_datetime, time_ago
from toot.utils.language import language_name from witchie.utils.language import language_name
from toot.entities import Status from witchie.entities import Status
from toot.tui.scroll import Scrollable, ScrollBar from witchie.tui.scroll import Scrollable, ScrollBar
from toot.tui.utils import highlight_keys from witchie.tui.utils import highlight_keys
from toot.tui.widgets import SelectableText, SelectableColumns from witchie.tui.widgets import SelectableText, SelectableColumns
logger = logging.getLogger("toot") logger = logging.getLogger("witchie")
class Timeline(urwid.Columns): class Timeline(urwid.Columns):

View file

@ -88,17 +88,17 @@ def copy_to_clipboard(screen: urwid.raw_display.Screen, text: str):
screen.flush() screen.flush()
def get_max_toot_chars(instance, default=500): def get_max_post_chars(instance, default=500):
# Mastodon # Mastodon
# https://docs.joinmastodon.org/entities/Instance/#max_characters # https://docs.joinmastodon.org/entities/Instance/#max_characters
max_toot_chars = deep_get(instance, ["configuration", "statuses", "max_characters"]) max_post_chars = deep_get(instance, ["configuration", "statuses", "max_characters"])
if isinstance(max_toot_chars, int): if isinstance(max_post_chars, int):
return max_toot_chars return max_post_chars
# Pleroma # Pleroma
max_toot_chars = instance.get("max_toot_chars") max_post_chars = instance.get("max_post_chars")
if isinstance(max_toot_chars, int): if isinstance(max_post_chars, int):
return max_toot_chars return max_post_chars
return default return default

View file

@ -9,7 +9,7 @@ import warnings
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from typing import Dict from typing import Dict
from toot.exceptions import ConsoleError from witchie.exceptions import ConsoleError
from urllib.parse import urlparse, urlencode, quote, unquote from urllib.parse import urlparse, urlencode, quote, unquote
@ -104,7 +104,7 @@ EDITOR_DIVIDER = "------------------------ >8 ------------------------"
EDITOR_INPUT_INSTRUCTIONS = f""" EDITOR_INPUT_INSTRUCTIONS = f"""
{EDITOR_DIVIDER} {EDITOR_DIVIDER}
Do not modify or remove the line above. Do not modify or remove the line above.
Enter your toot above it. Enter your post above it.
Everything below it will be ignored. Everything below it will be ignored.
""" """
@ -145,11 +145,11 @@ def delete_tmp_status_file():
def _tmp_status_path() -> str: def _tmp_status_path() -> str:
tmp_dir = tempfile.gettempdir() tmp_dir = tempfile.gettempdir()
return f"{tmp_dir}/.status.toot" return f"{tmp_dir}/.status.post"
def _use_existing_tmp_file(tmp_path) -> bool: def _use_existing_tmp_file(tmp_path) -> bool:
from toot.output import print_out from witchie.output import print_out
if os.path.exists(tmp_path): if os.path.exists(tmp_path):
print_out(f"<cyan>Found a draft status at: {tmp_path}</cyan>") print_out(f"<cyan>Found a draft status at: {tmp_path}</cyan>")
@ -179,15 +179,15 @@ def args_get_instance(instance, scheme, default=None):
def _warn_scheme_deprecated(): def _warn_scheme_deprecated():
from toot.output import print_err from witchie.output import print_err
print_err("\n".join([ print_err("\n".join([
"--disable-https flag is deprecated and will be removed.", "--disable-https flag is deprecated and will be removed.",
"Please specify the instance as URL instead.", "Please specify the instance as URL instead.",
"e.g. instead of writing:", "e.g. instead of writing:",
" toot instance unsafehost.com --disable-https", " witchie instance unsafehost.com --disable-https",
"instead write:", "instead write:",
" toot instance http://unsafehost.com\n" " witchie instance http://unsafehost.com\n"
])) ]))