mirror of
https://github.com/keanuplayz/dotfiles.git
synced 2024-08-15 02:33:12 +00:00
Merge pull request #277 from dmitmel/master
[pull] master from dmitmel:master
This commit is contained in:
commit
043d991aff
23 changed files with 197 additions and 141 deletions
|
@ -3,8 +3,7 @@
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from typing import Dict, Iterable, List, Protocol, TextIO, runtime_checkable
|
from typing import Dict, Iterator, List, Protocol, TextIO, runtime_checkable
|
||||||
|
|
||||||
|
|
||||||
__dir__ = os.path.dirname(__file__)
|
__dir__ = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
@ -12,16 +11,20 @@ __dir__ = os.path.dirname(__file__)
|
||||||
class Color:
|
class Color:
|
||||||
|
|
||||||
def __init__(self, r: int, g: int, b: int) -> None:
|
def __init__(self, r: int, g: int, b: int) -> None:
|
||||||
assert 0 <= r <= 0xff
|
if not (0 <= r <= 0xff):
|
||||||
assert 0 <= g <= 0xff
|
raise Exception("r component out of range")
|
||||||
assert 0 <= b <= 0xff
|
if not (0 <= g <= 0xff):
|
||||||
|
raise Exception("g component out of range")
|
||||||
|
if not (0 <= b <= 0xff):
|
||||||
|
raise Exception("b component out of range")
|
||||||
self.r = r
|
self.r = r
|
||||||
self.g = g
|
self.g = g
|
||||||
self.b = b
|
self.b = b
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_hex(cls, s: str) -> "Color":
|
def from_hex(cls, s: str) -> "Color":
|
||||||
assert len(s) == 6
|
if len(s) != 6:
|
||||||
|
raise Exception("hex color string must be 6 characters long")
|
||||||
return Color(int(s[0:2], 16), int(s[2:4], 16), int(s[4:6], 16))
|
return Color(int(s[0:2], 16), int(s[2:4], 16), int(s[4:6], 16))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -42,7 +45,7 @@ class Color:
|
||||||
else:
|
else:
|
||||||
raise IndexError("color component index out of range")
|
raise IndexError("color component index out of range")
|
||||||
|
|
||||||
def __iter__(self) -> Iterable[int]:
|
def __iter__(self) -> Iterator[int]:
|
||||||
yield self.r
|
yield self.r
|
||||||
yield self.g
|
yield self.g
|
||||||
yield self.b
|
yield self.b
|
||||||
|
@ -296,10 +299,6 @@ class ThemeGeneratorXfceTerminal(ThemeGenerator):
|
||||||
|
|
||||||
class ThemeGeneratorVscode(ThemeGenerator):
|
class ThemeGeneratorVscode(ThemeGenerator):
|
||||||
|
|
||||||
def file_name(self) -> str:
|
|
||||||
return "vscode-colorCustomizations.json"
|
|
||||||
|
|
||||||
def generate(self, theme: Theme, output: TextIO) -> None:
|
|
||||||
ANSI_COLOR_NAMES = [
|
ANSI_COLOR_NAMES = [
|
||||||
"Black",
|
"Black",
|
||||||
"Red",
|
"Red",
|
||||||
|
@ -311,6 +310,11 @@ class ThemeGeneratorVscode(ThemeGenerator):
|
||||||
"White",
|
"White",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def file_name(self) -> str:
|
||||||
|
return "vscode-colorCustomizations.json"
|
||||||
|
|
||||||
|
def generate(self, theme: Theme, output: TextIO) -> None:
|
||||||
|
|
||||||
colors: Dict[str, str] = {
|
colors: Dict[str, str] = {
|
||||||
"terminal.background": theme.bg.css_hex,
|
"terminal.background": theme.bg.css_hex,
|
||||||
"terminal.foreground": theme.fg.css_hex,
|
"terminal.foreground": theme.fg.css_hex,
|
||||||
|
@ -320,8 +324,8 @@ class ThemeGeneratorVscode(ThemeGenerator):
|
||||||
}
|
}
|
||||||
|
|
||||||
for is_bright in [False, True]:
|
for is_bright in [False, True]:
|
||||||
for color_index, color_name in enumerate(ANSI_COLOR_NAMES):
|
for color_index, color_name in enumerate(self.ANSI_COLOR_NAMES):
|
||||||
color = theme.ansi_colors[color_index + int(is_bright) * len(ANSI_COLOR_NAMES)]
|
color = theme.ansi_colors[color_index + int(is_bright) * len(self.ANSI_COLOR_NAMES)]
|
||||||
colors["terminal.ansi" + ("Bright" if is_bright else "") + color_name] = color.css_hex
|
colors["terminal.ansi" + ("Bright" if is_bright else "") + color_name] = color.css_hex
|
||||||
|
|
||||||
json.dump(colors, output, ensure_ascii=False, indent=2)
|
json.dump(colors, output, ensure_ascii=False, indent=2)
|
||||||
|
@ -341,7 +345,7 @@ class ThemeGeneratorIterm(ThemeGenerator):
|
||||||
output.write('<plist version="1.0">\n')
|
output.write('<plist version="1.0">\n')
|
||||||
output.write("<dict>\n")
|
output.write("<dict>\n")
|
||||||
|
|
||||||
def write_color(key_name, color):
|
def write_color(key_name: str, color: Color) -> None:
|
||||||
r, g, b = (float(component) / 0xff for component in color)
|
r, g, b = (float(component) / 0xff for component in color)
|
||||||
output.write(" <key>{} Color</key>\n".format(key_name))
|
output.write(" <key>{} Color</key>\n".format(key_name))
|
||||||
output.write(" <dict>\n")
|
output.write(" <dict>\n")
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
{
|
{
|
||||||
"root": "scripts",
|
"root": "scripts",
|
||||||
"extraPaths": ["script-resources"]
|
"extraPaths": ["script-resources"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "script-resources/welcome",
|
||||||
|
"extraPaths": ["script-resources/welcome"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,27 @@
|
||||||
[flake8]
|
[flake8]
|
||||||
|
|
||||||
|
select =
|
||||||
|
# <https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes>
|
||||||
|
E W
|
||||||
|
# <https://flake8.pycqa.org/en/latest/user/error-codes.html#error-violation-codes>
|
||||||
|
F
|
||||||
|
# <https://github.com/zheller/flake8-quotes#warnings>
|
||||||
|
Q0
|
||||||
|
# <https://github.com/sco1/flake8-annotations#table-of-warnings>
|
||||||
|
ANN
|
||||||
|
# <https://github.com/PyCQA/pep8-naming#error-codes>
|
||||||
|
N8
|
||||||
|
# <https://github.com/gforcada/flake8-isort#error-codes>
|
||||||
|
I0
|
||||||
|
# <https://github.com/PyCQA/flake8-commas/>
|
||||||
|
C8
|
||||||
|
# <https://bandit.readthedocs.io/en/latest/plugins/index.html#complete-test-plugin-listing>
|
||||||
|
S
|
||||||
|
|
||||||
|
# Also see this:
|
||||||
|
# <https://github.com/wemake-services/wemake-python-styleguide>
|
||||||
|
# <https://wemake-python-stylegui.de/en/latest/pages/usage/violations/index.html>
|
||||||
|
|
||||||
ignore =
|
ignore =
|
||||||
# Indent is not a multiple of 4
|
# Indent is not a multiple of 4
|
||||||
E111
|
E111
|
||||||
|
@ -13,8 +36,17 @@ ignore =
|
||||||
# Line too long
|
# Line too long
|
||||||
E501
|
E501
|
||||||
# `except` without an exception type
|
# `except` without an exception type
|
||||||
E722
|
# NOTE: write `except Exception` (or `except BaseException` if absolutely
|
||||||
|
# necessary) instead
|
||||||
|
# E722
|
||||||
# Newline before a binary operator
|
# Newline before a binary operator
|
||||||
W503
|
W503
|
||||||
# Newline after a binary operator
|
# Newline after a binary operator
|
||||||
W504
|
W504
|
||||||
|
|
||||||
|
# Missing type annotations for `self` and `cls` respectively
|
||||||
|
ANN101 ANN102
|
||||||
|
|
||||||
|
inline-quotes = "
|
||||||
|
multiline-quotes = "
|
||||||
|
docstring-quotes = "
|
||||||
|
|
|
@ -3,7 +3,6 @@ based_on_style = google
|
||||||
column_limit = 99
|
column_limit = 99
|
||||||
indent_width = 2
|
indent_width = 2
|
||||||
continuation_indent_width = 2
|
continuation_indent_width = 2
|
||||||
blank_lines_between_top_level_imports_and_variables = 2
|
|
||||||
dedent_closing_brackets = true
|
dedent_closing_brackets = true
|
||||||
coalesce_brackets = true
|
coalesce_brackets = true
|
||||||
spaces_around_power_operator = true
|
spaces_around_power_operator = true
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import sys
|
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Iterable, NoReturn
|
from typing import Iterable, NoReturn
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
# <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
|
from typing import IO, Any
|
||||||
|
|
||||||
|
|
||||||
def read_bool(buf: IO[bytes]) -> bool:
|
def read_bool(buf: IO[bytes]) -> bool:
|
||||||
|
|
|
@ -2,7 +2,7 @@ from math import *
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
|
|
||||||
|
|
||||||
def factors(n):
|
def factors(n: int) -> "set[int]":
|
||||||
result = set()
|
result = set()
|
||||||
for i in range(1, int(sqrt(n)) + 1):
|
for i in range(1, int(sqrt(n)) + 1):
|
||||||
if n % i == 0:
|
if n % i == 0:
|
||||||
|
@ -11,7 +11,7 @@ def factors(n):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def solve_quadratic(a, b, c):
|
def solve_quadratic(a: int, b: int, c: int) -> None:
|
||||||
if a == 0:
|
if a == 0:
|
||||||
raise Exception("not a quadratic equation")
|
raise Exception("not a quadratic equation")
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from colorama import Fore, Style, ansi
|
from colorama import Fore, Style, ansi
|
||||||
|
|
||||||
|
COLORS: List[str] = [ansi.code_to_chars(30 + color_index) for color_index in range(0, 8)]
|
||||||
COLORS = [ansi.code_to_chars(30 + color_index) for color_index in range(0, 8)]
|
|
||||||
|
|
||||||
|
|
||||||
def colored(string, *colors):
|
def colored(string: str, *colors: str) -> str:
|
||||||
return "".join(colors + (string, Style.RESET_ALL))
|
return "".join(colors + (string, Style.RESET_ALL))
|
||||||
|
|
||||||
|
|
||||||
def bright_colored(string, *colors):
|
def bright_colored(string: str, *colors: str) -> str:
|
||||||
return "".join(colors + (Style.BRIGHT, string, Style.RESET_ALL))
|
return "".join(colors + (Style.BRIGHT, string, Style.RESET_ALL))
|
||||||
|
|
||||||
|
|
||||||
def colorize_percent(percent, warning, critical, inverse=False):
|
def colorize_percent(
|
||||||
COLORS = [Fore.GREEN, Fore.YELLOW, Fore.RED]
|
percent: float, warning: float, critical: float, inverse: bool = False
|
||||||
|
) -> str:
|
||||||
|
colors = [Fore.GREEN, Fore.YELLOW, Fore.RED]
|
||||||
|
|
||||||
color_index = 0 if percent < warning else 1 if percent < critical else 2
|
color_index = 0 if percent < warning else 1 if percent < critical else 2
|
||||||
if inverse:
|
if inverse:
|
||||||
color_index = 2 - color_index
|
color_index = 2 - color_index
|
||||||
|
|
||||||
return colored("%.2f%%" % percent, COLORS[color_index])
|
return colored("%.2f%%" % percent, colors[color_index])
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
def humanize_timedelta(timedelta):
|
from datetime import timedelta
|
||||||
result = []
|
from typing import List, Tuple
|
||||||
|
|
||||||
|
|
||||||
|
def humanize_timedelta(timedelta: timedelta) -> str:
|
||||||
|
result: List[str] = []
|
||||||
|
|
||||||
days = timedelta.days
|
days = timedelta.days
|
||||||
mm, ss = divmod(timedelta.seconds, 60)
|
mm, ss = divmod(timedelta.seconds, 60)
|
||||||
hh, mm = divmod(mm, 60)
|
hh, mm = divmod(mm, 60)
|
||||||
|
|
||||||
def plural(n):
|
def plural(n: int) -> Tuple[int, str]:
|
||||||
return n, "s" if abs(n) != 1 else ""
|
return n, "s" if abs(n) != 1 else ""
|
||||||
|
|
||||||
if days > 0:
|
if days > 0:
|
||||||
|
@ -20,7 +24,7 @@ def humanize_timedelta(timedelta):
|
||||||
return ", ".join(result)
|
return ", ".join(result)
|
||||||
|
|
||||||
|
|
||||||
def humanize_bytes(bytes):
|
def humanize_bytes(bytes: int) -> str:
|
||||||
units = ["B", "kB", "MB", "GB"]
|
units = ["B", "kB", "MB", "GB"]
|
||||||
|
|
||||||
factor = 1
|
factor = 1
|
||||||
|
|
|
@ -5,7 +5,6 @@ import re
|
||||||
from colors import COLORS, Style
|
from colors import COLORS, Style
|
||||||
from system_info import get_system_info
|
from system_info import get_system_info
|
||||||
|
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
logo_lines, info_lines = get_system_info()
|
logo_lines, info_lines = get_system_info()
|
||||||
|
|
|
@ -3,17 +3,17 @@ import platform
|
||||||
import socket
|
import socket
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from getpass import getuser
|
from getpass import getuser
|
||||||
|
from typing import Dict, List, Optional, Tuple, cast
|
||||||
|
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
from colors import Fore, Style, bright_colored, colored, colorize_percent
|
from colors import Fore, Style, bright_colored, colored, colorize_percent
|
||||||
from humanize import humanize_bytes, humanize_timedelta
|
from humanize import humanize_bytes, humanize_timedelta
|
||||||
|
|
||||||
|
|
||||||
def get_system_info():
|
def get_system_info() -> Tuple[List[str], List[str]]:
|
||||||
info_lines = []
|
info_lines: List[str] = []
|
||||||
|
|
||||||
def info(name, value, *format_args):
|
def info(name: str, value: str, *format_args) -> None:
|
||||||
line = bright_colored(name + ":", Fore.YELLOW) + " " + value
|
line = bright_colored(name + ":", Fore.YELLOW) + " " + value
|
||||||
if format_args:
|
if format_args:
|
||||||
line = line % format_args
|
line = line % format_args
|
||||||
|
@ -66,27 +66,27 @@ def get_system_info():
|
||||||
return logo_lines, info_lines
|
return logo_lines, info_lines
|
||||||
|
|
||||||
|
|
||||||
def _get_hostname():
|
def _get_hostname() -> str:
|
||||||
hostname = socket.gethostname()
|
hostname = socket.gethostname()
|
||||||
return hostname
|
return hostname
|
||||||
|
|
||||||
|
|
||||||
def _get_uptime():
|
def _get_uptime() -> timedelta:
|
||||||
return datetime.now() - datetime.fromtimestamp(psutil.boot_time())
|
return datetime.now() - datetime.fromtimestamp(psutil.boot_time())
|
||||||
|
|
||||||
|
|
||||||
def _get_users():
|
def _get_users() -> str:
|
||||||
users = {}
|
users: Dict[str, List[str]] = {}
|
||||||
|
|
||||||
for user in psutil.users():
|
for user in psutil.users():
|
||||||
name = user.name
|
name: str = user.name
|
||||||
terminal = user.terminal
|
terminal: str = user.terminal
|
||||||
if name in users:
|
if name in users:
|
||||||
users[name].append(terminal)
|
users[name].append(terminal)
|
||||||
else:
|
else:
|
||||||
users[name] = [terminal]
|
users[name] = [terminal]
|
||||||
|
|
||||||
result = []
|
result: List[str] = []
|
||||||
|
|
||||||
for name in users:
|
for name in users:
|
||||||
terminals = users[name]
|
terminals = users[name]
|
||||||
|
@ -102,13 +102,13 @@ def _get_users():
|
||||||
return ", ".join(result)
|
return ", ".join(result)
|
||||||
|
|
||||||
|
|
||||||
def _get_shell():
|
def _get_shell() -> Optional[str]:
|
||||||
return os.environ.get("SHELL")
|
return os.environ.get("SHELL")
|
||||||
|
|
||||||
|
|
||||||
def _get_cpu_usage():
|
def _get_cpu_usage() -> Optional[str]:
|
||||||
try:
|
try:
|
||||||
percent = psutil.cpu_percent()
|
percent = cast(float, psutil.cpu_percent())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Error in _get_cpu_usage:", e)
|
print("Error in _get_cpu_usage:", e)
|
||||||
return None
|
return None
|
||||||
|
@ -116,7 +116,7 @@ def _get_cpu_usage():
|
||||||
return colorize_percent(percent, warning=60, critical=80)
|
return colorize_percent(percent, warning=60, critical=80)
|
||||||
|
|
||||||
|
|
||||||
def _get_memory():
|
def _get_memory() -> Tuple[str, str, str]:
|
||||||
memory = psutil.virtual_memory()
|
memory = psutil.virtual_memory()
|
||||||
return (
|
return (
|
||||||
humanize_bytes(memory.used),
|
humanize_bytes(memory.used),
|
||||||
|
@ -125,8 +125,8 @@ def _get_memory():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _get_disks():
|
def _get_disks() -> List[Tuple[str, str, str, str]]:
|
||||||
result = []
|
result: List[Tuple[str, str, str, str]] = []
|
||||||
|
|
||||||
for disk in psutil.disk_partitions(all=False):
|
for disk in psutil.disk_partitions(all=False):
|
||||||
if psutil.WINDOWS and ("cdrom" in disk.opts or disk.fstype == ""):
|
if psutil.WINDOWS and ("cdrom" in disk.opts or disk.fstype == ""):
|
||||||
|
@ -146,7 +146,7 @@ def _get_disks():
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _get_battery():
|
def _get_battery() -> Optional[Tuple[str, str]]:
|
||||||
if not hasattr(psutil, "sensors_battery"):
|
if not hasattr(psutil, "sensors_battery"):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -167,8 +167,8 @@ def _get_battery():
|
||||||
return colorize_percent(percent, critical=10, warning=20, inverse=True), status
|
return colorize_percent(percent, critical=10, warning=20, inverse=True), status
|
||||||
|
|
||||||
|
|
||||||
def _get_local_ipv4_addresses():
|
def _get_local_ipv4_addresses() -> List[Tuple[str, str]]:
|
||||||
result = []
|
result: List[Tuple[str, str]] = []
|
||||||
|
|
||||||
for interface, addresses in psutil.net_if_addrs().items():
|
for interface, addresses in psutil.net_if_addrs().items():
|
||||||
for address in addresses:
|
for address in addresses:
|
||||||
|
@ -184,7 +184,7 @@ def _get_local_ipv4_addresses():
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _get_distro_info():
|
def _get_distro_info() -> Tuple[str, str, str, str]:
|
||||||
if psutil.WINDOWS:
|
if psutil.WINDOWS:
|
||||||
return "windows", platform.system(), platform.release(), ""
|
return "windows", platform.system(), platform.release(), ""
|
||||||
elif psutil.OSX:
|
elif psutil.OSX:
|
||||||
|
@ -194,9 +194,13 @@ def _get_distro_info():
|
||||||
sw_vers = plistlib.load(f)
|
sw_vers = plistlib.load(f)
|
||||||
return "mac", sw_vers["ProductName"], sw_vers["ProductVersion"], ""
|
return "mac", sw_vers["ProductName"], sw_vers["ProductVersion"], ""
|
||||||
elif _is_android():
|
elif _is_android():
|
||||||
from subprocess import check_output
|
import subprocess
|
||||||
|
|
||||||
android_version = check_output(["getprop", "ro.build.version.release"])
|
android_version = subprocess.run(
|
||||||
|
["getprop", "ro.build.version.release"],
|
||||||
|
check=True,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
).stdout
|
||||||
return "android", "Android", android_version.decode().strip(), ""
|
return "android", "Android", android_version.decode().strip(), ""
|
||||||
elif psutil.LINUX:
|
elif psutil.LINUX:
|
||||||
import distro
|
import distro
|
||||||
|
@ -206,5 +210,5 @@ def _get_distro_info():
|
||||||
raise NotImplementedError("unsupported OS")
|
raise NotImplementedError("unsupported OS")
|
||||||
|
|
||||||
|
|
||||||
def _is_android():
|
def _is_android() -> bool:
|
||||||
return os.path.isdir("/system/app") and os.path.isdir("/system/priv-app")
|
return os.path.isdir("/system/app") and os.path.isdir("/system/priv-app")
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
from configparser import ConfigParser
|
|
||||||
import json
|
import json
|
||||||
from typing import Any, Generator, Optional, Union
|
import os
|
||||||
|
import sys
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
from configparser import ConfigParser
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Generator, Optional, Union
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_REGISTRY_DUMP_URL = "https://stronghold.crosscode.ru/~ccbot/emote-registry.json"
|
DEFAULT_REGISTRY_DUMP_URL = "https://stronghold.crosscode.ru/~ccbot/emote-registry.json"
|
||||||
|
|
||||||
if os.name == "posix":
|
if os.name == "posix":
|
||||||
|
@ -49,7 +47,8 @@ def emote_downloader_and_iterator() -> Generator[str, None, None]:
|
||||||
with urllib.request.urlopen(registry_dump_url, timeout=10) as response:
|
with urllib.request.urlopen(registry_dump_url, timeout=10) as response:
|
||||||
emote_registry_data = json.load(response)
|
emote_registry_data = json.load(response)
|
||||||
|
|
||||||
assert emote_registry_data["version"] == 1
|
if emote_registry_data["version"] != 1:
|
||||||
|
raise Exception("unsupported emote registry version")
|
||||||
|
|
||||||
allow_nsfw = config.getboolean("default", "allow_nsfw", fallback=False)
|
allow_nsfw = config.getboolean("default", "allow_nsfw", fallback=False)
|
||||||
emotes = [emote for emote in emote_registry_data["list"] if emote["safe"] or allow_nsfw]
|
emotes = [emote for emote in emote_registry_data["list"] if emote["safe"] or allow_nsfw]
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import base64
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import base64
|
||||||
|
import sys
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from typing import IO
|
from typing import IO
|
||||||
from Crypto.Cipher import AES
|
|
||||||
from Crypto import Random
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
from Crypto import Random
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
|
||||||
CC_ENCRYPTION_MARKER_BYTES = b"[-!_0_!-]"
|
CC_ENCRYPTION_MARKER_BYTES = b"[-!_0_!-]"
|
||||||
CC_ENCRYPTION_PASSPHRASE = b":_.NaN0"
|
CC_ENCRYPTION_PASSPHRASE = b":_.NaN0"
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main() -> None:
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
# NOTE: Empty help strings are necessary for subparsers to show up in help.
|
# NOTE: Empty help strings are necessary for subparsers to show up in help.
|
||||||
subparsers = parser.add_subparsers(required=True, metavar="COMMAND")
|
subparsers = parser.add_subparsers(required=True, metavar="COMMAND")
|
||||||
|
@ -34,14 +34,15 @@ def main():
|
||||||
|
|
||||||
def cmd_pipe_decrypt(args: argparse.Namespace) -> None:
|
def cmd_pipe_decrypt(args: argparse.Namespace) -> None:
|
||||||
input_file: IO[bytes] = (
|
input_file: IO[bytes] = (
|
||||||
sys.stdin.buffer if args.input_file == "-" else open(args.input_file, 'rb')
|
sys.stdin.buffer if args.input_file == "-" else open(args.input_file, "rb")
|
||||||
)
|
)
|
||||||
output_file: IO[bytes] = (
|
output_file: IO[bytes] = (
|
||||||
sys.stdout.buffer if args.output_file == "-" else open(args.output_file, 'wb')
|
sys.stdout.buffer if args.output_file == "-" else open(args.output_file, "wb")
|
||||||
)
|
)
|
||||||
|
|
||||||
encrypted = input_file.read()
|
encrypted = input_file.read()
|
||||||
assert encrypted.startswith(CC_ENCRYPTION_MARKER_BYTES)
|
if not encrypted.startswith(CC_ENCRYPTION_MARKER_BYTES):
|
||||||
|
raise Exception()
|
||||||
encrypted = encrypted[len(CC_ENCRYPTION_MARKER_BYTES):]
|
encrypted = encrypted[len(CC_ENCRYPTION_MARKER_BYTES):]
|
||||||
decrypted = CryptoJsBridge.decrypt(encrypted, CC_ENCRYPTION_PASSPHRASE)
|
decrypted = CryptoJsBridge.decrypt(encrypted, CC_ENCRYPTION_PASSPHRASE)
|
||||||
output_file.write(decrypted)
|
output_file.write(decrypted)
|
||||||
|
@ -49,10 +50,10 @@ def cmd_pipe_decrypt(args: argparse.Namespace) -> None:
|
||||||
|
|
||||||
def cmd_pipe_encrypt(args: argparse.Namespace) -> None:
|
def cmd_pipe_encrypt(args: argparse.Namespace) -> None:
|
||||||
input_file: IO[bytes] = (
|
input_file: IO[bytes] = (
|
||||||
sys.stdin.buffer if args.input_file == "-" else open(args.input_file, 'rb')
|
sys.stdin.buffer if args.input_file == "-" else open(args.input_file, "rb")
|
||||||
)
|
)
|
||||||
output_file: IO[bytes] = (
|
output_file: IO[bytes] = (
|
||||||
sys.stdout.buffer if args.output_file == "-" else open(args.output_file, 'wb')
|
sys.stdout.buffer if args.output_file == "-" else open(args.output_file, "wb")
|
||||||
)
|
)
|
||||||
|
|
||||||
decrypted = input_file.read()
|
decrypted = input_file.read()
|
||||||
|
@ -87,7 +88,8 @@ class CryptoJsBridge:
|
||||||
"""
|
"""
|
||||||
Extended from <https://gist.github.com/gsakkis/4546068/1d65cea035562e36da2cc160d6a9e4821b553fa8#file-aes-py-L49-L57>.
|
Extended from <https://gist.github.com/gsakkis/4546068/1d65cea035562e36da2cc160d6a9e4821b553fa8#file-aes-py-L49-L57>.
|
||||||
"""
|
"""
|
||||||
assert len(salt) == cls.SALT_SIZE
|
if len(salt) != cls.SALT_SIZE:
|
||||||
|
raise Exception("invalid salt length")
|
||||||
data += salt
|
data += salt
|
||||||
key = md5(data).digest()
|
key = md5(data).digest()
|
||||||
final_key = key
|
final_key = key
|
||||||
|
@ -114,7 +116,8 @@ class CryptoJsBridge:
|
||||||
Equivalent to `CryptoJS.AES.decrypt(encrypted, passphrase).toString(CryptoJS.enc.Utf8)`.
|
Equivalent to `CryptoJS.AES.decrypt(encrypted, passphrase).toString(CryptoJS.enc.Utf8)`.
|
||||||
"""
|
"""
|
||||||
encrypted = base64.b64decode(encrypted)
|
encrypted = base64.b64decode(encrypted)
|
||||||
assert encrypted.startswith(cls.SALTED_MARKER)
|
if not encrypted.startswith(cls.SALTED_MARKER):
|
||||||
|
raise Exception("expected salt marker")
|
||||||
encrypted = encrypted[len(cls.SALTED_MARKER):]
|
encrypted = encrypted[len(cls.SALTED_MARKER):]
|
||||||
salt, ciphertext = encrypted[:cls.SALT_SIZE], encrypted[cls.SALT_SIZE:]
|
salt, ciphertext = encrypted[:cls.SALT_SIZE], encrypted[cls.SALT_SIZE:]
|
||||||
key_iv = cls.bytes_to_key(passphrase, salt, cls.KEY_SIZE + cls.IV_SIZE)
|
key_iv = cls.bytes_to_key(passphrase, salt, cls.KEY_SIZE + cls.IV_SIZE)
|
||||||
|
|
|
@ -3,16 +3,16 @@
|
||||||
# https://discord.com/developers/docs/reference#snowflakes
|
# https://discord.com/developers/docs/reference#snowflakes
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import colorama
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
import colorama
|
||||||
|
|
||||||
DISCORD_EPOCH = 1420070400000 # milliseconds
|
DISCORD_EPOCH = 1420070400000 # milliseconds
|
||||||
|
|
||||||
user_snowflake = int(sys.argv[1])
|
user_snowflake = int(sys.argv[1])
|
||||||
|
|
||||||
|
|
||||||
def print_field(name, value):
|
def print_field(name: str, value: object) -> None:
|
||||||
print(
|
print(
|
||||||
"{}{}:{} {}".format(colorama.Style.BRIGHT, name.rjust(21), colorama.Style.RESET_ALL, value)
|
"{}{}:{} {}".format(colorama.Style.BRIGHT, name.rjust(21), colorama.Style.RESET_ALL, value)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import discord
|
|
||||||
import sys
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
from typing import Optional, cast
|
||||||
|
|
||||||
|
import discord
|
||||||
|
|
||||||
guild_id = int(sys.argv[1])
|
guild_id = int(sys.argv[1])
|
||||||
voice_channel_id = int(sys.argv[2])
|
voice_channel_id = int(sys.argv[2])
|
||||||
|
@ -16,17 +17,17 @@ bot = discord.Client()
|
||||||
|
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_ready():
|
async def on_ready() -> None:
|
||||||
print("logged in as {0} ({0.id})".format(bot.user))
|
print("logged in as {0} ({0.id})".format(bot.user))
|
||||||
|
|
||||||
guild: discord.Guild = bot.get_guild(guild_id)
|
guild: Optional[discord.Guild] = bot.get_guild(guild_id)
|
||||||
if guild is None:
|
if guild is None:
|
||||||
raise Exception("guild not found")
|
raise Exception("guild not found")
|
||||||
voice_channel: discord.VoiceChannel = guild.get_channel(voice_channel_id)
|
voice_channel: Optional[discord.VoiceChannel] = guild.get_channel(voice_channel_id)
|
||||||
if voice_channel is None:
|
if voice_channel is None:
|
||||||
raise Exception("channel not found")
|
raise Exception("channel not found")
|
||||||
|
|
||||||
voice_client = await voice_channel.connect()
|
voice_client = cast(discord.voice_client.VoiceClient, await voice_channel.connect())
|
||||||
print("connected to {0} ({0.id}) in {1} ({1.id})".format(voice_channel, guild))
|
print("connected to {0} ({0.id}) in {1} ({1.id})".format(voice_channel, guild))
|
||||||
|
|
||||||
source = discord.FFmpegPCMAudio(pulseaudio_device, before_options="-f pulse")
|
source = discord.FFmpegPCMAudio(pulseaudio_device, before_options="-f pulse")
|
||||||
|
|
|
@ -4,15 +4,16 @@
|
||||||
# <https://discord.com/developers/docs/resources/user#user-object#get-user>
|
# <https://discord.com/developers/docs/resources/user#user-object#get-user>
|
||||||
# <https://discord.com/developers/docs/reference>
|
# <https://discord.com/developers/docs/reference>
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import urllib.request
|
|
||||||
import urllib.error
|
|
||||||
import colorama
|
|
||||||
import time
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
|
import colorama
|
||||||
|
|
||||||
DISCORD_EPOCH = 1420070400000 # milliseconds
|
DISCORD_EPOCH = 1420070400000 # milliseconds
|
||||||
# https://discord.com/developers/docs/resources/user#user-object-user-flags
|
# https://discord.com/developers/docs/resources/user#user-object-user-flags
|
||||||
|
@ -37,17 +38,17 @@ parser.add_argument("user_snowflake", type=int)
|
||||||
parser.add_argument("--bot-token", type=str)
|
parser.add_argument("--bot-token", type=str)
|
||||||
parser.add_argument("--image-size", type=int)
|
parser.add_argument("--image-size", type=int)
|
||||||
parser.add_argument("--get-prop", type=str)
|
parser.add_argument("--get-prop", type=str)
|
||||||
parser.add_argument("--api-response", action='store_true')
|
parser.add_argument("--api-response", action="store_true")
|
||||||
cli_args = parser.parse_args()
|
cli_args = parser.parse_args()
|
||||||
|
|
||||||
user_snowflake = cli_args.user_snowflake
|
user_snowflake: int = cli_args.user_snowflake
|
||||||
|
|
||||||
bot_token = cli_args.bot_token
|
bot_token: Optional[str] = cli_args.bot_token
|
||||||
if bot_token is None:
|
if bot_token is None:
|
||||||
with open(os.path.expanduser("~/.config/dotfiles/discord-tools-bot-token.txt")) as f:
|
with open(os.path.expanduser("~/.config/dotfiles/discord-tools-bot-token.txt")) as f:
|
||||||
bot_token = f.read().strip()
|
bot_token = f.read().strip()
|
||||||
|
|
||||||
image_size = cli_args.image_size
|
image_size: Optional[int] = cli_args.image_size
|
||||||
if not (image_size is None or (image_size > 0 and image_size & (image_size - 1)) == 0):
|
if not (image_size is None or (image_size > 0 and image_size & (image_size - 1)) == 0):
|
||||||
parser.error("image_size must be greater than zero and a power of two")
|
parser.error("image_size must be greater than zero and a power of two")
|
||||||
|
|
||||||
|
@ -70,10 +71,10 @@ except urllib.error.HTTPError as err:
|
||||||
|
|
||||||
if cli_args.api_response:
|
if cli_args.api_response:
|
||||||
json.dump(raw_data, sys.stdout, ensure_ascii=False, indent=2)
|
json.dump(raw_data, sys.stdout, ensure_ascii=False, indent=2)
|
||||||
sys.stdout.write('\n')
|
sys.stdout.write("\n")
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
data = {}
|
data: Dict[str, str] = {}
|
||||||
|
|
||||||
data["ID"] = raw_data["id"]
|
data["ID"] = raw_data["id"]
|
||||||
data["Name"] = "{}#{}".format(raw_data["username"], raw_data["discriminator"])
|
data["Name"] = "{}#{}".format(raw_data["username"], raw_data["discriminator"])
|
||||||
|
@ -108,7 +109,7 @@ user_flags = raw_data["public_flags"]
|
||||||
if user_flags == 0:
|
if user_flags == 0:
|
||||||
data["Flags"] = "<none>"
|
data["Flags"] = "<none>"
|
||||||
else:
|
else:
|
||||||
user_flag_names = []
|
user_flag_names: List[str] = []
|
||||||
for flag_name, bitmask in DISCORD_FLAGS.items():
|
for flag_name, bitmask in DISCORD_FLAGS.items():
|
||||||
if user_flags & bitmask:
|
if user_flags & bitmask:
|
||||||
user_flag_names.append(flag_name)
|
user_flag_names.append(flag_name)
|
||||||
|
|
|
@ -4,12 +4,11 @@
|
||||||
# <https://www.dropbox.com/sh/uscmj9y3cjfwpsr/AAD35_ZZu64EBi0awLA07fxga?dl=0>
|
# <https://www.dropbox.com/sh/uscmj9y3cjfwpsr/AAD35_ZZu64EBi0awLA07fxga?dl=0>
|
||||||
# <https://github.com/credomane/factoriomodsettings/blob/master/src/FactorioModSettings.js>
|
# <https://github.com/credomane/factoriomodsettings/blob/master/src/FactorioModSettings.js>
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
import struct
|
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
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 factorio.property_tree
|
import factorio.property_tree
|
||||||
|
@ -23,7 +22,8 @@ with open(Path.home() / ".factorio" / "mods" / "mod-settings.dat", "rb") as f:
|
||||||
version_main, version_major, version_minor, version_developer = struct.unpack("<HHHH", f.read(8))
|
version_main, version_major, version_minor, version_developer = struct.unpack("<HHHH", f.read(8))
|
||||||
|
|
||||||
always_false_flag = factorio.property_tree.read_bool(f)
|
always_false_flag = factorio.property_tree.read_bool(f)
|
||||||
assert not always_false_flag
|
if always_false_flag:
|
||||||
|
raise Exception("the always-False-flag is True for some reason")
|
||||||
|
|
||||||
deserialized_data = {
|
deserialized_data = {
|
||||||
"factorio_version": {
|
"factorio_version": {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import argparse
|
import argparse
|
||||||
from pathlib import Path
|
|
||||||
from typing import Union
|
|
||||||
import plyvel
|
|
||||||
import json
|
|
||||||
from sys import stdout
|
|
||||||
import base64
|
import base64
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from sys import stdout
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
import plyvel
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
encoding_names = ["utf8", "base16", "base32", "base64", "base85"]
|
encoding_names = ["utf8", "base16", "base32", "base64", "base85"]
|
||||||
|
@ -32,7 +32,7 @@ def bytes_to_json(b: bytes, encoding: str) -> Union[str, list[int]]:
|
||||||
elif encoding == "base85":
|
elif encoding == "base85":
|
||||||
return base64.b85encode(b).decode("ascii")
|
return base64.b85encode(b).decode("ascii")
|
||||||
else:
|
else:
|
||||||
assert False
|
raise Exception("unreachable")
|
||||||
|
|
||||||
|
|
||||||
key_encoding: str = cli_args.key_encoding or cli_args.encoding
|
key_encoding: str = cli_args.key_encoding or cli_args.encoding
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# Taken from <https://unix.stackexchange.com/a/509417/411555>
|
# Taken from <https://unix.stackexchange.com/a/509417/411555>
|
||||||
|
|
||||||
import gi
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import gi
|
||||||
|
|
||||||
gi.require_version("Gtk", "3.0")
|
gi.require_version("Gtk", "3.0")
|
||||||
from gi.repository import Gtk, Gio, GLib
|
from gi.repository import Gio, GLib, Gtk
|
||||||
|
|
||||||
|
|
||||||
rec_mgr = Gtk.RecentManager.get_default()
|
rec_mgr = Gtk.RecentManager.get_default()
|
||||||
for arg in sys.argv[1:]:
|
for arg in sys.argv[1:]:
|
||||||
|
|
|
@ -39,12 +39,13 @@
|
||||||
# created by this script.
|
# created by this script.
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import mwclient
|
|
||||||
import json
|
|
||||||
from urllib.parse import urlencode
|
|
||||||
import html
|
import html
|
||||||
|
import json
|
||||||
import re
|
import re
|
||||||
|
from typing import Dict, List, cast
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
import mwclient
|
||||||
|
|
||||||
LANG = "en"
|
LANG = "en"
|
||||||
LANG_TEXT_DIRECTION = "ltr"
|
LANG_TEXT_DIRECTION = "ltr"
|
||||||
|
@ -116,7 +117,7 @@ MODULES_PRELOAD_SCRIPTS = {
|
||||||
|
|
||||||
|
|
||||||
# ported from <https://github.com/wikimedia/mediawiki/blob/c15ded31a6ca79fa65c00d151a7220632ad90b6d/includes/parser/Sanitizer.php#L1205-L1222>
|
# ported from <https://github.com/wikimedia/mediawiki/blob/c15ded31a6ca79fa65c00d151a7220632ad90b6d/includes/parser/Sanitizer.php#L1205-L1222>
|
||||||
def escape_css_class(class_str):
|
def escape_css_class(class_str: str) -> str:
|
||||||
class_str = re.sub(
|
class_str = re.sub(
|
||||||
r"""(^[0-9\-])|[\x00-\x20!"#$%&'()*+,.\/:;<=>?@[\]^`{|}~]|\xA0""",
|
r"""(^[0-9\-])|[\x00-\x20!"#$%&'()*+,.\/:;<=>?@[\]^`{|}~]|\xA0""",
|
||||||
"_",
|
"_",
|
||||||
|
@ -127,8 +128,8 @@ def escape_css_class(class_str):
|
||||||
return class_str
|
return class_str
|
||||||
|
|
||||||
|
|
||||||
def json_dumps_compact(data):
|
def json_dumps_compact(data: object) -> str:
|
||||||
return json.dumps(data, indent=None, separators=(",", ":"))
|
return json.dumps(data, indent=None, separators=(",", ":"), ensure_ascii=False)
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
@ -147,7 +148,7 @@ cli_args = parser.parse_args()
|
||||||
site = mwclient.Site(cli_args.site, scheme=cli_args.scheme)
|
site = mwclient.Site(cli_args.site, scheme=cli_args.scheme)
|
||||||
|
|
||||||
|
|
||||||
def get_load_script_url(**args):
|
def get_load_script_url(**args: object) -> str:
|
||||||
return "{path}load{ext}?{args}".format(
|
return "{path}load{ext}?{args}".format(
|
||||||
path=site.path,
|
path=site.path,
|
||||||
ext=site.ext,
|
ext=site.ext,
|
||||||
|
@ -177,7 +178,11 @@ result = site.post(
|
||||||
)["parse"]
|
)["parse"]
|
||||||
|
|
||||||
|
|
||||||
def get_modules(page_modules, added_modules_dict, blocked_modules_dict={}):
|
def get_modules(
|
||||||
|
page_modules: List[str],
|
||||||
|
added_modules_dict: Dict[str, List[str]],
|
||||||
|
blocked_modules_dict: Dict[str, List[str]] = {}
|
||||||
|
) -> List[str]:
|
||||||
modules = page_modules + added_modules_dict[cli_args.skin]
|
modules = page_modules + added_modules_dict[cli_args.skin]
|
||||||
for blocked_module in blocked_modules_dict.get(cli_args.skin, []):
|
for blocked_module in blocked_modules_dict.get(cli_args.skin, []):
|
||||||
try:
|
try:
|
||||||
|
@ -272,7 +277,7 @@ rendered_html = """\
|
||||||
),
|
),
|
||||||
skin=html.escape(cli_args.skin),
|
skin=html.escape(cli_args.skin),
|
||||||
page_class=html.escape(escape_css_class(result["displaytitle"])),
|
page_class=html.escape(escape_css_class(result["displaytitle"])),
|
||||||
title=html.escape(result["displaytitle"]),
|
title=html.escape(cast(str, result["displaytitle"])),
|
||||||
indicators_html="\n".join([
|
indicators_html="\n".join([
|
||||||
'<div id="mw-indicator-{}" class="mw-indicator">{}</div>'.format(
|
'<div id="mw-indicator-{}" class="mw-indicator">{}</div>'.format(
|
||||||
indicator["name"], indicator["*"]
|
indicator["name"], indicator["*"]
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import gi
|
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
|
import gi
|
||||||
|
|
||||||
gi.require_version("Gtk", "3.0")
|
gi.require_version("Gtk", "3.0")
|
||||||
from gi.repository import Gtk, Gdk, Pango
|
gi.require_version("Gdk", "3.0")
|
||||||
|
from gi.repository import Gdk, Gtk, Pango
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("message", type=str, nargs="+")
|
parser.add_argument("message", type=str, nargs="+")
|
||||||
|
@ -24,13 +24,13 @@ scrolled_window.add(label)
|
||||||
window.add(scrolled_window)
|
window.add(scrolled_window)
|
||||||
|
|
||||||
|
|
||||||
def on_key_release(target, event):
|
def on_key_release(_target, event) -> None:
|
||||||
key = event.keyval
|
key = event.keyval
|
||||||
if key in [Gdk.KEY_Escape, Gdk.KEY_q, Gdk.KEY_Q]:
|
if key in [Gdk.KEY_Escape, Gdk.KEY_q, Gdk.KEY_Q]:
|
||||||
window.close()
|
window.close()
|
||||||
|
|
||||||
|
|
||||||
def on_configure(target, event):
|
def on_configure(target, event) -> None:
|
||||||
if target != window or event.type != Gdk.EventType.CONFIGURE:
|
if target != window or event.type != Gdk.EventType.CONFIGURE:
|
||||||
return
|
return
|
||||||
font_desc = Pango.FontDescription()
|
font_desc = Pango.FontDescription()
|
||||||
|
|
|
@ -7,15 +7,15 @@
|
||||||
# TODO: Update the menu on player status changes.
|
# TODO: Update the menu on player status changes.
|
||||||
|
|
||||||
import math
|
import math
|
||||||
import gi
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import gi
|
||||||
|
|
||||||
gi.require_version("Playerctl", "2.0")
|
gi.require_version("Playerctl", "2.0")
|
||||||
gi.require_version("Gtk", "3.0")
|
gi.require_version("Gtk", "3.0")
|
||||||
gi.require_version("Gdk", "3.0")
|
gi.require_version("Gdk", "3.0")
|
||||||
gi.require_version("Pango", "1.0")
|
gi.require_version("Pango", "1.0")
|
||||||
from gi.repository import Playerctl, Gtk, Gdk, GLib, Pango
|
from gi.repository import Gdk, GLib, Gtk, Pango, Playerctl
|
||||||
|
|
||||||
# Larger priority values will make the player with this name appear higher in
|
# Larger priority values will make the player with this name appear higher in
|
||||||
# the menu. The default priority is 0.
|
# the menu. The default priority is 0.
|
||||||
|
@ -39,7 +39,7 @@ PLAYER_PLAYBACK_STATUS_EMOJIS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def humanize_duration(duration):
|
def humanize_duration(duration: float) -> str:
|
||||||
minutes, seconds = divmod(math.floor(duration), 60)
|
minutes, seconds = divmod(math.floor(duration), 60)
|
||||||
hours, minutes = divmod(minutes, 60)
|
hours, minutes = divmod(minutes, 60)
|
||||||
text = "{:02}:{:02}".format(minutes, seconds)
|
text = "{:02}:{:02}".format(minutes, seconds)
|
||||||
|
|
|
@ -7,15 +7,14 @@
|
||||||
# <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 os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
from configparser import ConfigParser
|
|
||||||
import tempfile
|
|
||||||
import shutil
|
import shutil
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from typing import Optional, Tuple, Generator
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from configparser import ConfigParser
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Generator, Optional, Tuple
|
||||||
|
|
||||||
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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue