mirror of
https://github.com/keanuplayz/dotfiles.git
synced 2024-08-15 02:33:12 +00:00
Compare commits
No commits in common. "1cc52196dd402ff737d2811e144468d49cd59d75" and "0d82ed488bdbb1c3612c437c8d57f5dc3ecb812f" have entirely different histories.
1cc52196dd
...
0d82ed488b
7 changed files with 55 additions and 92 deletions
|
@ -2,32 +2,19 @@ import sys
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Iterable, NoReturn
|
|
||||||
|
|
||||||
|
|
||||||
if os.name == "posix":
|
if os.name == "posix":
|
||||||
DOTFILES_CONFIG_DIR: Path = Path.home() / ".config" / "dotfiles"
|
DOTFILES_CONFIG_DIR = Path.home() / ".config" / "dotfiles"
|
||||||
DOTFILES_CACHE_DIR: Path = Path.home() / ".cache" / "dotfiles"
|
DOTFILES_CACHE_DIR = Path.home() / ".cache" / "dotfiles"
|
||||||
|
|
||||||
|
|
||||||
def platform_not_supported_error() -> NoReturn:
|
def platform_not_supported_error():
|
||||||
raise Exception("platform '{}' is not supported!".format(sys.platform))
|
raise Exception("platform '{}' is not supported!".format(sys.platform))
|
||||||
|
|
||||||
|
|
||||||
def run_chooser(
|
def run_chooser(choices, prompt=None, async_read=False):
|
||||||
choices: Iterable[str], prompt: str = None, async_read: bool = False
|
if sys.platform == "darwin":
|
||||||
) -> int:
|
|
||||||
supports_result_index = True
|
|
||||||
if os.isatty(sys.stderr.fileno()):
|
|
||||||
process_args = [
|
|
||||||
"fzf",
|
|
||||||
"--with-nth=2..",
|
|
||||||
"--height=50%",
|
|
||||||
"--reverse",
|
|
||||||
"--tiebreak=index",
|
|
||||||
]
|
|
||||||
supports_result_index = False
|
|
||||||
elif sys.platform == "darwin":
|
|
||||||
process_args = ["choose", "-i"]
|
process_args = ["choose", "-i"]
|
||||||
elif os.name == "posix":
|
elif os.name == "posix":
|
||||||
process_args = ["rofi", "-dmenu", "-i", "-format", "i"]
|
process_args = ["rofi", "-dmenu", "-i", "-format", "i"]
|
||||||
|
@ -43,23 +30,24 @@ def run_chooser(
|
||||||
)
|
)
|
||||||
|
|
||||||
with chooser_process.stdin as pipe:
|
with chooser_process.stdin as pipe:
|
||||||
for index, choice in enumerate(choices):
|
for choice in choices:
|
||||||
assert "\n" not in choice
|
assert "\n" not in choice
|
||||||
if not supports_result_index:
|
|
||||||
pipe.write(str(index).encode())
|
|
||||||
pipe.write(b" ")
|
|
||||||
pipe.write(choice.encode())
|
pipe.write(choice.encode())
|
||||||
pipe.write(b"\n")
|
pipe.write(b"\n")
|
||||||
|
|
||||||
exit_code: int = chooser_process.wait()
|
exit_code = chooser_process.wait()
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
raise Exception("chooser process failed with exit code {}".format(exit_code))
|
raise Exception("chooser process failed with exit code {}".format(exit_code))
|
||||||
|
|
||||||
chosen_index = int(chooser_process.stdout.read().strip().split()[0])
|
chosen_index = int(
|
||||||
|
# an extra newline is inserted by rofi for whatever reason
|
||||||
|
chooser_process.stdout.read().rstrip(b"\n")
|
||||||
|
)
|
||||||
|
|
||||||
return chosen_index
|
return chosen_index
|
||||||
|
|
||||||
|
|
||||||
def send_notification(title: str, message: str, url: str = None) -> None:
|
def send_notification(title, message, url=None):
|
||||||
if sys.platform == "darwin":
|
if sys.platform == "darwin":
|
||||||
process_args = [
|
process_args = [
|
||||||
"terminal-notifier",
|
"terminal-notifier",
|
||||||
|
@ -85,7 +73,7 @@ def send_notification(title: str, message: str, url: str = None) -> None:
|
||||||
subprocess.run(process_args, check=True)
|
subprocess.run(process_args, check=True)
|
||||||
|
|
||||||
|
|
||||||
def set_clipboard(text: str) -> None:
|
def set_clipboard(text):
|
||||||
# TODO: somehow merge program selection with the logic in `zsh/functions.zsh`
|
# TODO: somehow merge program selection with the logic in `zsh/functions.zsh`
|
||||||
if sys.platform == "darwin":
|
if sys.platform == "darwin":
|
||||||
process_args = ["pbcopy"]
|
process_args = ["pbcopy"]
|
||||||
|
|
|
@ -5,22 +5,21 @@
|
||||||
# <https://www.devdungeon.com/content/working-binary-data-python>
|
# <https://www.devdungeon.com/content/working-binary-data-python>
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
from typing import Any, IO
|
|
||||||
|
|
||||||
|
|
||||||
def read_bool(buf: IO[bytes]) -> bool:
|
def read_bool(buf):
|
||||||
return buf.read(1)[0] == 1
|
return buf.read(1)[0] == 1
|
||||||
|
|
||||||
|
|
||||||
def read_number(buf: IO[bytes]) -> float:
|
def read_number(buf):
|
||||||
return struct.unpack("<d", buf.read(8))[0]
|
return struct.unpack("<d", buf.read(8))[0]
|
||||||
|
|
||||||
|
|
||||||
def _read_length(buf: IO[bytes]) -> int:
|
def _read_length(buf):
|
||||||
return struct.unpack("<I", buf.read(4))[0]
|
return struct.unpack("<I", buf.read(4))[0]
|
||||||
|
|
||||||
|
|
||||||
def read_string(buf: IO[bytes]) -> str:
|
def read_string(buf):
|
||||||
is_empty = read_bool(buf)
|
is_empty = read_bool(buf)
|
||||||
if is_empty:
|
if is_empty:
|
||||||
return ""
|
return ""
|
||||||
|
@ -30,25 +29,25 @@ def read_string(buf: IO[bytes]) -> str:
|
||||||
return buf.read(len_).decode("utf8")
|
return buf.read(len_).decode("utf8")
|
||||||
|
|
||||||
|
|
||||||
def read_dictionary(buf: IO[bytes]) -> dict[str, Any]:
|
def read_dictionary(buf):
|
||||||
len_ = _read_length(buf)
|
len_ = _read_length(buf)
|
||||||
value: dict[str, Any] = {}
|
value = {}
|
||||||
for _ in range(len_):
|
for _ in range(len_):
|
||||||
key = read_string(buf)
|
key = read_string(buf)
|
||||||
value[key] = read(buf)
|
value[key] = read(buf)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def read_list(buf: IO[bytes]) -> list[Any]:
|
def read_list(buf):
|
||||||
len_ = _read_length(buf)
|
len_ = _read_length(buf)
|
||||||
value: list[Any] = []
|
value = []
|
||||||
for _ in range(len_):
|
for _ in range(len_):
|
||||||
read_string(buf)
|
read_string(buf)
|
||||||
value.append(read(buf))
|
value.append(read(buf))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def read(buf: IO[bytes]) -> Any:
|
def read(buf):
|
||||||
type_, _any_type_flag = buf.read(2)
|
type_, _any_type_flag = buf.read(2)
|
||||||
if type_ == 0:
|
if type_ == 0:
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -24,11 +24,10 @@ def humanize_bytes(bytes):
|
||||||
units = ["B", "kB", "MB", "GB"]
|
units = ["B", "kB", "MB", "GB"]
|
||||||
|
|
||||||
factor = 1
|
factor = 1
|
||||||
unit = ""
|
for _unit in units:
|
||||||
for unit in units:
|
|
||||||
next_factor = factor << 10
|
next_factor = factor << 10
|
||||||
if bytes < next_factor:
|
if bytes < next_factor:
|
||||||
break
|
break
|
||||||
factor = next_factor
|
factor = next_factor
|
||||||
|
|
||||||
return "%.2f %s" % (float(bytes) / factor, unit)
|
return "%.2f %s" % (float(bytes) / factor, _unit)
|
||||||
|
|
|
@ -2,10 +2,8 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
import json
|
import json
|
||||||
from typing import Any, Generator, Optional, Union
|
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
|
||||||
|
@ -17,25 +15,23 @@ DEFAULT_REGISTRY_DUMP_URL = "https://stronghold.crosscode.ru/~ccbot/emote-regist
|
||||||
|
|
||||||
|
|
||||||
if os.name == "posix":
|
if os.name == "posix":
|
||||||
config_path: Path = (
|
config_path = (
|
||||||
common_script_utils.DOTFILES_CONFIG_DIR / "copy-crosscode-emoji-url.ini"
|
common_script_utils.DOTFILES_CONFIG_DIR / "copy-crosscode-emoji-url.ini"
|
||||||
)
|
)
|
||||||
default_registry_dump_file: Path = (
|
default_registry_dump_file = common_script_utils.DOTFILES_CACHE_DIR / "dotfiles"
|
||||||
common_script_utils.DOTFILES_CACHE_DIR / "dotfiles"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
common_script_utils.platform_not_supported_error()
|
common_script_utils.platform_not_supported_error()
|
||||||
config = ConfigParser(interpolation=None)
|
config = ConfigParser(interpolation=None)
|
||||||
config.read(config_path)
|
config.read(config_path)
|
||||||
|
|
||||||
|
|
||||||
emotes: list[dict[str, Any]] = []
|
emotes = []
|
||||||
|
|
||||||
|
|
||||||
def emote_downloader_and_iterator() -> Generator[str, None, None]:
|
def emote_downloader_and_iterator():
|
||||||
global emotes
|
global emotes
|
||||||
|
|
||||||
registry_dump_file: Optional[Union[str, Path]] = config.get(
|
registry_dump_file = config.get(
|
||||||
"default", "ccbot_emote_registry_dump_file", fallback=None
|
"default", "ccbot_emote_registry_dump_file", fallback=None
|
||||||
)
|
)
|
||||||
if registry_dump_file is not None:
|
if registry_dump_file is not None:
|
||||||
|
@ -47,7 +43,6 @@ def emote_downloader_and_iterator() -> Generator[str, None, None]:
|
||||||
"default", "ccbot_emote_registry_dump_url", fallback=DEFAULT_REGISTRY_DUMP_URL
|
"default", "ccbot_emote_registry_dump_url", fallback=DEFAULT_REGISTRY_DUMP_URL
|
||||||
)
|
)
|
||||||
|
|
||||||
emote_registry_data: dict[str, Any]
|
|
||||||
try:
|
try:
|
||||||
with open(registry_dump_file, "r") as f:
|
with open(registry_dump_file, "r") as f:
|
||||||
emote_registry_data = json.load(f)
|
emote_registry_data = json.load(f)
|
||||||
|
@ -71,8 +66,8 @@ chosen_index = common_script_utils.run_chooser(
|
||||||
if chosen_index >= 0:
|
if chosen_index >= 0:
|
||||||
chosen_emote = emotes[chosen_index]
|
chosen_emote = emotes[chosen_index]
|
||||||
|
|
||||||
emote_url: urllib.parse.ParseResult = urllib.parse.urlparse(chosen_emote["url"])
|
emote_url = urllib.parse.urlparse(chosen_emote["url"])
|
||||||
emote_url_query: dict[str, list[str]] = urllib.parse.parse_qs(emote_url.query)
|
emote_url_query = urllib.parse.parse_qs(emote_url.query)
|
||||||
|
|
||||||
if config.getboolean("default", "add_emote_name_to_url", fallback=False):
|
if config.getboolean("default", "add_emote_name_to_url", fallback=False):
|
||||||
emote_url_query["name"] = [chosen_emote["name"]]
|
emote_url_query["name"] = [chosen_emote["name"]]
|
||||||
|
@ -83,12 +78,10 @@ if chosen_index >= 0:
|
||||||
if default_emote_image_size is not None:
|
if default_emote_image_size is not None:
|
||||||
emote_url_query["size"] = [str(default_emote_image_size)]
|
emote_url_query["size"] = [str(default_emote_image_size)]
|
||||||
|
|
||||||
emote_url_query_str = urllib.parse.urlencode(emote_url_query, doseq=True)
|
emote_url_query = urllib.parse.urlencode(emote_url_query, doseq=True)
|
||||||
emote_url_str = urllib.parse.urlunparse(
|
emote_url = urllib.parse.urlunparse(emote_url._replace(query=emote_url_query))
|
||||||
emote_url._replace(query=emote_url_query_str)
|
|
||||||
)
|
|
||||||
|
|
||||||
common_script_utils.set_clipboard(emote_url_str)
|
common_script_utils.set_clipboard(emote_url)
|
||||||
|
|
||||||
common_script_utils.send_notification(
|
common_script_utils.send_notification(
|
||||||
os.path.basename(__file__),
|
os.path.basename(__file__),
|
||||||
|
|
|
@ -16,10 +16,6 @@ import factorio.property_tree
|
||||||
|
|
||||||
with open(Path.home() / ".factorio" / "mods" / "mod-settings.dat", "rb") as f:
|
with open(Path.home() / ".factorio" / "mods" / "mod-settings.dat", "rb") as f:
|
||||||
|
|
||||||
version_main: int
|
|
||||||
version_major: int
|
|
||||||
version_minor: int
|
|
||||||
version_developer: int
|
|
||||||
version_main, version_major, version_minor, version_developer = struct.unpack(
|
version_main, version_major, version_minor, version_developer = struct.unpack(
|
||||||
"<HHHH", f.read(8)
|
"<HHHH", f.read(8)
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
# helper script for query-bookmarks.sh
|
# helper script for query-bookmarks.sh
|
||||||
# currently supports only Firefox
|
# currently supports only Firefox
|
||||||
# useful links:
|
# useful links:
|
||||||
# <http://kb.mozillazine.org/Profiles.ini_file>
|
# http://kb.mozillazine.org/Profiles.ini_file
|
||||||
# <https://stackoverflow.com/a/740183/12005228>
|
# https://stackoverflow.com/a/740183/12005228
|
||||||
# <https://wiki.mozilla.org/Places:BookmarksComments>
|
# https://wiki.mozilla.org/Places:BookmarksComments
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
@ -14,16 +14,16 @@ from configparser import ConfigParser
|
||||||
import tempfile
|
import tempfile
|
||||||
import shutil
|
import shutil
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from typing import Optional, Tuple, Generator
|
import collections
|
||||||
|
|
||||||
sys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", "script-resources"))
|
sys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", "script-resources"))
|
||||||
import common_script_utils
|
import common_script_utils
|
||||||
|
|
||||||
|
|
||||||
if sys.platform == "darwin":
|
if sys.platform == "darwin":
|
||||||
firefox_home: Path = Path.home() / "Library" / "Application Support" / "Firefox"
|
firefox_home = Path.home() / "Library" / "Application Support" / "Firefox"
|
||||||
elif os.name == "posix":
|
elif os.name == "posix":
|
||||||
firefox_home: Path = Path.home() / ".mozilla" / "firefox"
|
firefox_home = Path.home() / ".mozilla" / "firefox"
|
||||||
else:
|
else:
|
||||||
common_script_utils.platform_not_supported_error()
|
common_script_utils.platform_not_supported_error()
|
||||||
|
|
||||||
|
@ -31,17 +31,15 @@ else:
|
||||||
profiles_config = ConfigParser(interpolation=None)
|
profiles_config = ConfigParser(interpolation=None)
|
||||||
profiles_config.read(firefox_home / "profiles.ini")
|
profiles_config.read(firefox_home / "profiles.ini")
|
||||||
|
|
||||||
installs_sections: list[str] = [
|
installs_sections = [s for s in profiles_config.sections() if s.startswith("Install")]
|
||||||
s for s in profiles_config.sections() if s.startswith("Install")
|
|
||||||
]
|
|
||||||
if not installs_sections:
|
if not installs_sections:
|
||||||
raise Exception("no Firefox installations detected!")
|
raise Exception("no Firefox installations detected!")
|
||||||
if len(installs_sections) > 1:
|
if len(installs_sections) > 1:
|
||||||
raise Exception("multiple Firefox installations are not supported!")
|
raise Exception("multiple Firefox installations are not supported!")
|
||||||
profile_dir: Path = firefox_home / profiles_config.get(installs_sections[0], "Default")
|
profile_dir = firefox_home / profiles_config.get(installs_sections[0], "Default")
|
||||||
|
|
||||||
# should places.sqlite be used instead?
|
# should places.sqlite be used instead?
|
||||||
db_path: Path = profile_dir / "weave" / "bookmarks.sqlite"
|
db_path = profile_dir / "weave" / "bookmarks.sqlite"
|
||||||
if not db_path.is_file():
|
if not db_path.is_file():
|
||||||
raise Exception("'{}' is not a file".format(db_path))
|
raise Exception("'{}' is not a file".format(db_path))
|
||||||
|
|
||||||
|
@ -52,22 +50,17 @@ if not db_path.is_file():
|
||||||
db_copy_fd, db_copy_path = tempfile.mkstemp(prefix="bookmarks.", suffix=".sqlite")
|
db_copy_fd, db_copy_path = tempfile.mkstemp(prefix="bookmarks.", suffix=".sqlite")
|
||||||
os.close(db_copy_fd)
|
os.close(db_copy_fd)
|
||||||
|
|
||||||
chooser_entries: list[Tuple[str, str, Optional[str]]] = []
|
chooser_entries = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
shutil.copyfile(db_path, db_copy_path)
|
shutil.copyfile(db_path, db_copy_path)
|
||||||
db = sqlite3.connect(db_copy_path)
|
db = sqlite3.connect(db_copy_path)
|
||||||
|
|
||||||
urls: dict[int, str] = {}
|
urls = {}
|
||||||
url_id: int
|
|
||||||
url: str
|
|
||||||
for url_id, url in db.execute("SELECT id, url FROM urls"):
|
for url_id, url in db.execute("SELECT id, url FROM urls"):
|
||||||
urls[url_id] = url
|
urls[url_id] = url
|
||||||
|
|
||||||
folders: dict[str, Tuple[Optional[str], str]] = {}
|
folders = {}
|
||||||
folder_id: str
|
|
||||||
parent_folder_id: str
|
|
||||||
folder_title: str
|
|
||||||
for folder_id, parent_folder_id, folder_title in db.execute(
|
for folder_id, parent_folder_id, folder_title in db.execute(
|
||||||
"SELECT guid, parentGuid, title FROM items WHERE kind = 3 AND validity AND NOT isDeleted"
|
"SELECT guid, parentGuid, title FROM items WHERE kind = 3 AND validity AND NOT isDeleted"
|
||||||
):
|
):
|
||||||
|
@ -76,29 +69,24 @@ try:
|
||||||
folder_title,
|
folder_title,
|
||||||
)
|
)
|
||||||
|
|
||||||
url_title: str
|
|
||||||
url_id: int
|
|
||||||
url_keyword: str
|
|
||||||
parent_folder_id: str
|
|
||||||
for url_title, url_id, url_keyword, parent_folder_id in db.execute(
|
for url_title, url_id, url_keyword, parent_folder_id in db.execute(
|
||||||
"SELECT title, urlId, keyword, parentGuid FROM items WHERE kind = 1 AND validity AND NOT isDeleted"
|
"SELECT title, urlId, keyword, parentGuid FROM items WHERE kind = 1 AND validity AND NOT isDeleted"
|
||||||
):
|
):
|
||||||
url = urls[url_id]
|
url = urls[url_id]
|
||||||
|
|
||||||
folder_path = list[str]()
|
folder_path = collections.deque()
|
||||||
parent_folder_id_2: Optional[str] = parent_folder_id
|
while parent_folder_id is not None:
|
||||||
while parent_folder_id_2 is not None:
|
folder = folders.get(parent_folder_id, None)
|
||||||
folder = folders.get(parent_folder_id_2, None)
|
|
||||||
if folder is None:
|
if folder is None:
|
||||||
# broken folder structure?
|
# broken folder structure?
|
||||||
folder_path.clear()
|
folder_path.clear()
|
||||||
break
|
break
|
||||||
parent_folder_id_2, folder_title = folder
|
parent_folder_id, folder_title = folder
|
||||||
if folder_title is not None:
|
if folder_title is not None:
|
||||||
folder_path.append(folder_title)
|
folder_path.appendleft(folder_title)
|
||||||
|
|
||||||
folder_path_str = (
|
folder_path_str = (
|
||||||
("/" + "/".join(reversed(folder_path))) if len(folder_path) > 0 else None
|
("/" + "/".join(folder_path)) if len(folder_path) > 0 else None
|
||||||
)
|
)
|
||||||
|
|
||||||
chooser_entries.append((url_title, url, folder_path_str))
|
chooser_entries.append((url_title, url, folder_path_str))
|
||||||
|
@ -109,7 +97,7 @@ finally:
|
||||||
os.remove(db_copy_path)
|
os.remove(db_copy_path)
|
||||||
|
|
||||||
|
|
||||||
def chooser_entries_iter() -> Generator[str, None, None]:
|
def chooser_entries_iter():
|
||||||
for title, url, folder_path_str in chooser_entries:
|
for title, url, folder_path_str in chooser_entries:
|
||||||
entry_items = [title, url]
|
entry_items = [title, url]
|
||||||
if folder_path_str is not None:
|
if folder_path_str is not None:
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
|
||||||
def randbyte() -> int:
|
def randbyte():
|
||||||
return random.randrange(0, 256)
|
return random.randrange(0, 256)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue