diff --git a/.gitignore b/.gitignore index 510a54d..f920867 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,156 @@ -.mypy_cache/ -venv/ -__pycache__ -.env -dist +# Random *.log* Makefile +*monkeytype.sqlite3 + +# Editors .idea/ .vim/ -*monkeytype.sqlite3 .vscode/ + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg MANIFEST -arigram.egg-info/ + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + diff --git a/README.md b/README.md index 9ef7a25..8d7657a 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ A fork of [tg](https://github.com/paul-nameless/tg) -- a hackable telegram TUI c - [ ] profile pictures - [ ] message/chat archiving - [ ] better error reporting +- [ ] joining of groups and channels based on t.me links and @s ## Requirements @@ -81,8 +82,7 @@ mkdir -p ~/.local/src cd ~/.local/src git clone https://github.com/TruncatedDinosour/arigram.git cd arigram -pip install --upgrade --user -r requirements.txt -pip install --upgrade --user . +./do local ``` And add this to `~/.bashrc` or whatever POSIX complient shell you use: @@ -111,10 +111,16 @@ This option is recommended: ```sh cd ~/.local/src/arigram -git reset --hard # This discards every change you made +git reset --hard # This discards every change you made locally git pull -pip install --upgrade --user -r requirements.txt -pip install --upgrade --user . +./do upgrade +``` + +or if you want to keep local changes + +```sh +cd ~/.local/src/arigram +./do local ``` ## Configuration @@ -126,6 +132,7 @@ Config file should be stored at `~/.config/arigram/config.py`. This is simple py ```python # should start with + (plus) and contain country code PHONE = "[phone number in international format]" +# For enable `PHONE = "+1234567890"` ``` ### Advanced configuration: @@ -301,9 +308,7 @@ text/*; vim "%s" ## Keybindings -vi like keybindings are used in the project. Can be used commands like `4j` - 4 lines down. - -For navigation arrow keys also can be used. +ViM like keybindings are used in the project. Can be used commands like `4j` - 4 lines down. ### Chats: @@ -329,12 +334,14 @@ For navigation arrow keys also can be used. - `G`: move to the last msg (at the bottom) - `D`: download file - `l`: if video, pics or audio then open app specified in mailcap file, for example: - ```ini - # Images - image/png; qView "%s" - audio/*; mpv "%s" - ``` - if text, open in `less` (to view multiline msgs) + +```ini +# Images +image/png; qView "%s" +audio/*; mpv "%s" +``` + +If text, open in `less` (to view multiline msgs) - `e`: edit current msg - ``: select msg and jump one msg down (use for deletion or forwarding) - ``: same as space but jumps one msg up diff --git a/TODO.md b/TODO.md deleted file mode 100644 index a2c29cb..0000000 --- a/TODO.md +++ /dev/null @@ -1,7 +0,0 @@ -- [] local passwords: - -``` -- You need to save authorization keys on the disk. -- So you can take the local passcode, derive an encryption key from this passcode and use this key to encrypt the authorization keys before saving. So when you start the app you can't decrypt the keys (and start using the API) without providing a correct local passcode for that. You can also save some hash (like SHA256) of the original keys along so that you can check whether you've decrypted the correct keys or just garbage because of an incorrect passcode. -``` - diff --git a/arigram/__main__.py b/arigram/__main__.py index 0b1ab0b..58c3f04 100644 --- a/arigram/__main__.py +++ b/arigram/__main__.py @@ -1,10 +1,12 @@ import logging.handlers import signal +import sys import threading from curses import window, wrapper # type: ignore from functools import partial from types import FrameType +import arigram from arigram import config, update_handlers, utils from arigram.controllers import Controller from arigram.models import Model @@ -22,7 +24,7 @@ def run(tg: Tdlib, stdscr: window) -> None: del sig, frame log.info("Interrupt signal is handled and ignored on purpose.") - signal.signal(signal.SIGINT, interrupt_signal_handler) + signal.signal(signal.SIGINT, interrupt_signal_handler) # type: ignore model = Model(tg) status_view = StatusView(stdscr) @@ -46,19 +48,24 @@ def run(tg: Tdlib, stdscr: window) -> None: def parse_args() -> None: - import sys + """Parse CLI arguments""" - if len(sys.argv) > 1 and sys.argv[1] in ("-v", "--version"): - import arigram + if len(sys.argv) < 2: + return + if sys.argv[1] in ("-v", "--version"): print("Terminal Telegram client") print("Version:", arigram.__version__) - exit(0) + sys.exit() def main() -> None: + """Main function""" + parse_args() + utils.cleanup_cache() + tg = Tdlib( config.EXTRA_TDLIB_HEADEARS, api_id=config.API_ID, diff --git a/arigram/colors.py b/arigram/colours.py similarity index 97% rename from arigram/colors.py rename to arigram/colours.py index 2adb508..549be93 100644 --- a/arigram/colors.py +++ b/arigram/colours.py @@ -26,7 +26,7 @@ invisible = curses.A_INVIS dim = curses.A_DIM -def get_color(fg: int, bg: int) -> int: +def get_colour(fg: int, bg: int) -> int: """Returns the curses color pair for the given fg/bg combination.""" key = (fg, bg) diff --git a/arigram/config.py b/arigram/config.py index c95b9a2..0557d29 100644 --- a/arigram/config.py +++ b/arigram/config.py @@ -6,87 +6,75 @@ import mailcap import os import platform import runpy -from typing import Any, Dict, Optional +from typing import Any, Dict, List, Optional, Tuple _os_name = platform.system() _linux = "Linux" _global_mailcap = mailcap.getcaps() -CONFIG_DIR = os.path.expanduser("~/.config/arigram/") -CONFIG_FILE = os.path.join(CONFIG_DIR, "config.py") -FILES_DIR = os.path.expanduser("~/.cache/arigram/") -DRAFTS_FILE = os.path.join(FILES_DIR, "drafts.json") +CONFIG_DIR: str = os.path.expanduser("~/.config/arigram/") +CONFIG_FILE: str = os.path.join(CONFIG_DIR, "config.py") +FILES_DIR: str = os.path.expanduser("~/.cache/arigram/") +DRAFTS_FILE: str = os.path.join(FILES_DIR, "drafts.json") MAILCAP_FILE: Optional[str] = None -LOG_LEVEL = "INFO" -LOG_PATH = os.path.expanduser("~/.local/share/arigram/") +LOG_LEVEL: str = "INFO" +LOG_PATH: str = os.path.expanduser("~/.local/share/arigram/") -API_ID = "559815" -API_HASH = "fd121358f59d764c57c55871aa0807ca" +API_ID: str = "559815" +API_HASH: str = "fd121358f59d764c57c55871aa0807ca" -PHONE = None -ENC_KEY = "" +PHONE: Optional[str] = None +ENC_KEY: str = "" -TDLIB_PATH = None -TDLIB_VERBOSITY = 0 +TDLIB_PATH: Optional[str] = None +TDLIB_VERBOSITY: int = 0 -MAX_DOWNLOAD_SIZE = "10MB" +MAX_DOWNLOAD_SIZE: str = "10MB" -NOTIFY_FUNCTION = None +NOTIFY_FUNCTION: Optional[Any] = None -VIEW_TEXT_CMD = "less" -FZF = "fzf" +VIEW_TEXT_CMD: str = "less" -if _os_name == _linux: - # for more info see https://trac.ffmpeg.org/wiki/Capture/ALSA - VOICE_RECORD_CMD = ( - "ffmpeg -f alsa -i hw:0 -c:a libopus -b:a 32k {file_path}" - ) -else: - VOICE_RECORD_CMD = ( - "ffmpeg -f avfoundation -i ':0' -c:a libopus -b:a 32k {file_path}" - ) +# for more info see https://trac.ffmpeg.org/wiki/Capture/ALSA +VOICE_RECORD_CMD: str = ( + "ffmpeg -f alsa -i hw:0 -c:a libopus -b:a 32k {file_path}" + if _os_name == _linux + else "ffmpeg -f avfoundation -i ':0' -c:a libopus -b:a 32k {file_path}" +) - -EDITOR = os.environ.get("EDITOR", "vim") +EDITOR: str = os.environ.get("EDITOR", "vim") _, __MAILCAP_EDITOR = mailcap.findmatch(_global_mailcap, "text/markdown") if __MAILCAP_EDITOR: EDITOR = str(__MAILCAP_EDITOR["view"]).split(" ", 1)[0] -LONG_MSG_CMD = f"{EDITOR} '{{file_path}}'" +LONG_MSG_CMD: str = f"{EDITOR} '{{file_path}}'" -if _os_name == _linux: - DEFAULT_OPEN = "xdg-open {file_path}" -else: - DEFAULT_OPEN = "open {file_path}" - -if _os_name == _linux: - if os.environ.get("WAYLAND_DISPLAY"): - COPY_CMD = "wl-copy" - else: - COPY_CMD = "xclip -selection cliboard" -else: - COPY_CMD = "pbcopy" +DEFAULT_OPEN: str = ( + "xdg-open {file_path}" if _os_name == _linux else "open {file_path}" +) CHAT_FLAGS: Dict[str, str] = {} MSG_FLAGS: Dict[str, str] = {} -ICON_PATH = os.path.join(os.path.dirname(__file__), "resources", "arigram.png") +ICON_PATH: str = os.path.join( + os.path.dirname(__file__), "resources", "arigram.png" +) -URL_VIEW = None +URL_VIEW: Optional[str] = None -USERS_COLORS = tuple(range(2, 16)) +USERS_COLOURS: Tuple[int, ...] = tuple(range(2, 16)) -KEEP_MEDIA = 7 +KEEP_MEDIA: int = 7 -FILE_PICKER_CMD = None +FILE_PICKER_CMD: Optional[str] = None -DOWNLOAD_DIR = os.path.expanduser("~/Downloads/") +DOWNLOAD_DIR: str = os.path.expanduser("~/Downloads/") -EXTRA_FILE_CHOOSER_PATHS = ["..", "/", "~"] +EXTRA_FILE_CHOOSER_PATHS: List[str] = ["..", "/", "~"] CUSTOM_KEYBINDS: Dict[str, Dict[str, Any]] = {} diff --git a/arigram/controllers.py b/arigram/controllers.py index d7b0f9f..89155cd 100644 --- a/arigram/controllers.py +++ b/arigram/controllers.py @@ -196,7 +196,7 @@ class Controller: def quit(self) -> str: return "QUIT" - @bind(msg_handler, ["h", "^D"]) + @bind(msg_handler, ["h"]) def back(self) -> str: return "BACK" @@ -276,7 +276,7 @@ class Controller: if self.model.jump_bottom(): self.render_msgs() - @bind(msg_handler, ["j", "^B", "^N"], repeat_factor=True) + @bind(msg_handler, ["j"], repeat_factor=True) def next_msg(self, repeat_factor: int = 1) -> None: if self.model.next_msg(repeat_factor): self.render_msgs() @@ -285,7 +285,7 @@ class Controller: def jump_10_msgs_down(self) -> None: self.next_msg(10) - @bind(msg_handler, ["k", "^C", "^P"], repeat_factor=True) + @bind(msg_handler, ["k"], repeat_factor=True) def prev_msg(self, repeat_factor: int = 1) -> Optional[bool]: if self.model.prev_msg(repeat_factor): self.render_msgs() @@ -327,7 +327,7 @@ class Controller: f.write(insert_replied_msg(msg)) f.seek(0) s.call(config.LONG_MSG_CMD.format(file_path=shlex.quote(f.name))) - with open(f.name) as f: + with open(f.name) as f: # type: ignore if replied_msg := strip_replied_msg(f.read().strip()): self.model.view_all_msgs() self.tg.reply_message(chat_id, reply_to_msg, replied_msg) @@ -397,7 +397,7 @@ class Controller: ) as s: self.tg.send_chat_action(chat_id, ChatAction.chatActionTyping) s.call(config.LONG_MSG_CMD.format(file_path=shlex.quote(f.name))) - with open(f.name) as f: + with open(f.name) as f: # type: ignore if msg := f.read().strip(): self.model.send_message(text=msg) self.present_info("Message sent") @@ -442,7 +442,7 @@ class Controller: else: s.call(config.FILE_PICKER_CMD.format(file_path=f.name)) # type: ignore - with open(f.name) as f: + with open(f.name) as f: # type: ignore file_path = f.read().strip() with suspend(self.view) as s: @@ -617,7 +617,7 @@ class Controller: ) return self._open_msg(msg, cmd) - @bind(msg_handler, ["l", "^J"]) + @bind(msg_handler, ["l"]) def open_current_msg(self) -> None: """Open msg or file with cmd in mailcap""" msg = MsgProxy(self.model.current_msg) @@ -656,7 +656,7 @@ class Controller: f.write(msg.text_content) f.flush() s.call(f"{config.EDITOR} {f.name}") - with open(f.name) as f: + with open(f.name) as f: # type: ignore if text := f.read().strip(): self.model.edit_message(text=text) self.present_info("Message edited") @@ -668,18 +668,20 @@ class Controller: int(cols / 2), max(len(user.name) for user in users), ) - users_out = "\n".join( + users_out = [ f"{user.id}\t{user.name:<{limit}} | {user.status}" for user in sorted(users, key=lambda user: user.order) - ) - cmd = config.FZF + " -n 2" - if is_multiple: - cmd += " -m" + ] + fzf_flags = "-n 2" - with NamedTemporaryFile("r+") as tmp, suspend(self.view) as s: - s.run_with_input(f"{cmd} > {tmp.name}", users_out) - with open(tmp.name) as f: - return [int(line.split()[0]) for line in f.readlines()] + if is_multiple: + fzf_flags += " -m" + + with suspend(self.view): + return [ + int(uid.split()[0].strip()) + for uid in pyfzf.FzfPrompt().prompt(users_out, fzf_flags) + ] @bind(chat_handler, ["ns"]) def new_secret(self) -> None: @@ -789,7 +791,7 @@ class Controller: def view_contacts(self) -> None: self._get_user_ids() - @bind(chat_handler, ["l", "^J", "^E"]) + @bind(chat_handler, ["l"]) def handle_msgs(self) -> Optional[str]: rc = self.handle(msg_handler, 0.2) if rc == "QUIT": @@ -802,13 +804,13 @@ class Controller: if self.model.first_chat(): self.render() - @bind(chat_handler, ["j", "^B", "^N"], repeat_factor=True) + @bind(chat_handler, ["j"], repeat_factor=True) @bind(msg_handler, ["]"]) def next_chat(self, repeat_factor: int = 1) -> None: if self.model.next_chat(repeat_factor): self.render() - @bind(chat_handler, ["k", "^C", "^P"], repeat_factor=True) + @bind(chat_handler, ["k"], repeat_factor=True) @bind(msg_handler, ["["]) def prev_chat(self, repeat_factor: int = 1) -> None: if self.model.prev_chat(repeat_factor): diff --git a/arigram/tdlib.py b/arigram/tdlib.py index 5fcd9ab..ba863ed 100644 --- a/arigram/tdlib.py +++ b/arigram/tdlib.py @@ -5,19 +5,20 @@ from telegram.client import AsyncResult, Telegram class ChatAction(Enum): - chatActionTyping = "typing" + chatActionTyping = "is typing" chatActionCancel = "cancel" - chatActionRecordingVideo = "recording video" - chatActionUploadingVideo = "uploading video" - chatActionRecordingVoiceNote = "recording voice" - chatActionUploadingVoiceNote = "uploading voice" - chatActionUploadingPhoto = "uploading photo" - chatActionUploadingDocument = "uploading document" - chatActionChoosingLocation = "choosing location" - chatActionChoosingContact = "choosing contact" - chatActionStartPlayingGame = "start playing game" - chatActionRecordingVideoNote = "recording video" - chatActionUploadingVideoNote = "uploading video" + chatActionRecordingVideo = "is recording a video" + chatActionUploadingVideo = "is uploading a video" + chatActionRecordingVoiceNote = "is recording a voice message" + chatActionUploadingVoiceNote = "is uploading a voice message" + chatActionUploadingPhoto = "is uploading a photo" + chatActionUploadingDocument = "is uploading a document" + chatActionChoosingLocation = "is choosing a location" + chatActionChoosingContact = "is choosing a contact" + chatActionStartPlayingGame = "started playing a game" + chatActionRecordingVideoNote = "is recording a video" + chatActionUploadingVideoNote = "is uploading a video" + chatActionChoosingSticker = "is choosing a sticker" class ChatType(Enum): diff --git a/arigram/utils.py b/arigram/utils.py index 2ffd636..76d165d 100644 --- a/arigram/utils.py +++ b/arigram/utils.py @@ -21,6 +21,8 @@ from subprocess import CompletedProcess from types import TracebackType from typing import Any, Callable, Dict, Optional, Tuple, Type +from pyperclip import copy as copy_clipboard + from arigram import config log = logging.getLogger(__name__) @@ -237,9 +239,7 @@ def truncate_to_len(string: str, width: int) -> str: def copy_to_clipboard(text: str) -> None: - subprocess.run( - config.COPY_CMD, universal_newlines=True, input=text, shell=True - ) + copy_clipboard(text) class suspend: @@ -333,11 +333,11 @@ def pretty_ts(ts: int) -> str: @lru_cache(maxsize=256) -def get_color_by_str(user: str) -> int: +def get_colour_by_str(user: str) -> int: index = int(hashlib.sha1(user.encode()).hexdigest(), 16) % len( - config.USERS_COLORS + config.USERS_COLOURS ) - return config.USERS_COLORS[index] + return config.USERS_COLOURS[index] def cleanup_cache() -> None: diff --git a/arigram/views.py b/arigram/views.py index 29a04c7..244f53a 100644 --- a/arigram/views.py +++ b/arigram/views.py @@ -6,10 +6,10 @@ from typing import Any, Dict, List, Optional, Tuple, Union, cast from _curses import window # type: ignore from arigram import config -from arigram.colors import ( +from arigram.colours import ( bold, cyan, - get_color, + get_colour, magenta, reverse, white, @@ -19,7 +19,7 @@ from arigram.models import Model, UserModel from arigram.msg import MsgProxy from arigram.tdlib import ChatType, get_chat_type, is_group from arigram.utils import ( - get_color_by_str, + get_colour_by_str, num, string_len_dwc, truncate_to_len, @@ -75,7 +75,7 @@ class View: curses.start_color() curses.use_default_colors() # init white color first to initialize colors correctly - get_color(white, -1) + get_colour(white, -1) self.stdscr = stdscr self.chats = chat_view @@ -189,13 +189,13 @@ class ChatView: self.win.resize(self.h, self.w) def _msg_color(self, is_selected: bool = False) -> int: - color = get_color(white, -1) + color = get_colour(white, -1) if is_selected: return color | reverse return color def _unread_color(self, is_selected: bool = False) -> int: - color = get_color(magenta, -1) + color = get_colour(magenta, -1) if is_selected: return color | reverse return color @@ -204,9 +204,9 @@ class ChatView: self, is_selected: bool, title: str, user: Optional[str] ) -> Tuple[int, ...]: attrs = ( - get_color(cyan, -1), - get_color(get_color_by_str(title), -1), - get_color(get_color_by_str(user or ""), -1), + get_colour(cyan, -1), + get_colour(get_colour_by_str(title), -1), + get_colour(get_colour_by_str(user or ""), -1), self._msg_color(is_selected), ) if is_selected: @@ -222,7 +222,7 @@ class ChatView: self.win.vline(0, width, line, self.h) self.win.addstr( - 0, 0, title.center(width)[:width], get_color(cyan, -1) | bold + 0, 0, title.center(width)[:width], get_colour(cyan, -1) | bold ) for i, chat in enumerate(chats, 1): @@ -571,7 +571,7 @@ class MsgView: column += string_len_dwc(elem) self.win.addstr( - 0, 0, self._msg_title(chat), get_color(cyan, -1) | bold + 0, 0, self._msg_title(chat), get_colour(cyan, -1) | bold ) self._refresh() @@ -604,10 +604,10 @@ class MsgView: def _msg_attributes(self, is_selected: bool, user: str) -> Tuple[int, ...]: attrs = ( - get_color(cyan, -1), - get_color(get_color_by_str(user), -1), - get_color(yellow, -1), - get_color(white, -1), + get_colour(cyan, -1), + get_colour(get_colour_by_str(user), -1), + get_colour(yellow, -1), + get_colour(white, -1), ) if is_selected: diff --git a/do b/do index d9cd32a..1e6c74e 100755 --- a/do +++ b/do @@ -1,35 +1,31 @@ -#!/bin/bash +#!/usr/bin/env bash -set -e +set -xe -SRC=$(dirname $0) +main() { + SRC="$(dirname "$0")" + cd "$SRC" -cd $SRC - -ARG=${1:-""} - - -case $ARG in - build) - python3 -m pip install --upgrade setuptools wheel - python3 setup.py sdist bdist_wheel - python3 -m pip install --upgrade twine - python3 -m twine upload --repository testpypi dist/* - ;; + ARG=${1:-""} + case $ARG in review) gh pr create -f ;; push) isort arigram/*.py - black arigram + black . python3 -m poetry check python3 -m poetry lock - git diff > /tmp/arigram.diff + + $0 check + $0 local + + git diff >/tmp/arigram.diff git add -A - git commit -S + git commit -sa git push -u origin main ;; @@ -41,58 +37,23 @@ case $ARG in python3 -m pip install --user --upgrade . ;; - release) - CURRENT_VERSION=$(cat arigram/__init__.py | grep version | cut -d '"' -f 2) - echo Current version $CURRENT_VERSION - - NEW_VERSION=$(echo $CURRENT_VERSION | awk -F. '{print $1 "." $2+1 "." $3}') - echo New version $NEW_VERSION - sed -i '' "s|$CURRENT_VERSION|$NEW_VERSION|g" arigram/__init__.py - poetry version $NEW_VERSION - - git add -u arigram/__init__.py pyproject.toml - git commit -m "Release v$NEW_VERSION" - git tag v$NEW_VERSION - - poetry build - poetry publish -u $(pass show i/pypi | grep username | cut -d ' ' -f 2 | tr -d '\n') -p $(pass show i/pypi | head -n 1 | tr -d '\n') - git log --pretty=format:"%cn: %s" v$CURRENT_VERSION...v$NEW_VERSION | grep -v -e "Merge" | grep -v "Release"| awk '!x[$0]++' > changelog.md - git push origin master --tags - gh release create v$NEW_VERSION -F changelog.md - rm changelog.md - ;; - - release-brew) - CURRENT_VERSION=$(cat arigram/__init__.py | grep version | cut -d '"' -f 2) - echo Current version $CURRENT_VERSION - - URL="https://github.com/TruncatedDinosour/arigram/archive/refs/tags/v$CURRENT_VERSION.tar.gz" - echo $URL - wget $URL -O /tmp/arigram.tar.gz - HASH=$(sha256sum /tmp/arigram.tar.gz | cut -d ' ' -f 1) - rm /tmp/arigram.tar.gz - - cd /opt/homebrew/Library/Taps/TruncatedDinosour/dino-bar - sed -i '' "6s|.*| url \"https://github.com/TruncatedDinosour/arigram/archive/refs/tags/v$CURRENT_VERSION.tar.gz\"|" arigram.rb - sed -i '' "7s|.*| sha256 \"$HASH\"|" arigram.rb - - brew audit --new arigram - brew uninstall arigram || true - brew install arigram - brew test arigram - - git add -u arigram.rb - git commit -m "Release arigram.rb v$CURRENT_VERSION" - git push origin master + upgrade) + git reset --hard + git pull + $0 local ;; check) black . isort arigram/*.py - sh check.sh + chmod a+rx ./check.sh + ./check.sh ;; *) python3 -m arigram ;; -esac + esac +} + +main "$@" diff --git a/poetry.lock b/poetry.lock index db02f77..f14a58a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -115,14 +115,14 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "plumbum" -version = "1.7.1" +version = "1.7.2" description = "Plumbum: shell combinators library" category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [package.dependencies] -pypiwin32 = {version = "*", markers = "platform_system == \"Windows\" and platform_python_implementation != \"PyPy\""} +pywin32 = {version = "*", markers = "platform_system == \"Windows\" and platform_python_implementation != \"PyPy\""} [package.extras] dev = ["paramiko", "psutil", "pytest", "pytest-cov", "pytest-mock", "pytest-timeout"] @@ -164,17 +164,6 @@ category = "main" optional = false python-versions = "*" -[[package]] -name = "pypiwin32" -version = "223" -description = "" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -pywin32 = ">=223" - [[package]] name = "python-telegram" version = "0.15.0" @@ -185,7 +174,7 @@ python-versions = "*" [[package]] name = "pywin32" -version = "302" +version = "303" description = "Python for Window Extensions" category = "main" optional = false @@ -226,7 +215,7 @@ python-versions = ">=3.6" [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "061484270c70aa1136531e656408afb105e5d532473e9a642961595878ecbde9" +content-hash = "d09bad5856588aa83f522a7573c118d8f930c23020657822ec409d2d9da274b9" [metadata.files] appdirs = [ @@ -289,8 +278,8 @@ pathspec = [ {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, ] plumbum = [ - {file = "plumbum-1.7.1-py2.py3-none-any.whl", hash = "sha256:e97229fdf218698b4e9e939355f4d20d20bf57f8a02710f81f92442e2b2db038"}, - {file = "plumbum-1.7.1.tar.gz", hash = "sha256:3c0ac8c4ee57b2adddc82909d3c738a62ef5f77faf24ec7cb6f0a117e1679740"}, + {file = "plumbum-1.7.2-py2.py3-none-any.whl", hash = "sha256:0bbf431e31da988405de2fb36c3226f09c0c9cdf69c8480f8997f4b94b7370a1"}, + {file = "plumbum-1.7.2.tar.gz", hash = "sha256:0d1bf908076bbd0484d16412479cb97d6843069ee19f99e267e11dd980040523"}, ] pycodestyle = [ {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, @@ -307,24 +296,22 @@ pyfzf = [ pyperclip = [ {file = "pyperclip-1.8.2.tar.gz", hash = "sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57"}, ] -pypiwin32 = [ - {file = "pypiwin32-223-py3-none-any.whl", hash = "sha256:67adf399debc1d5d14dffc1ab5acacb800da569754fafdc576b2a039485aa775"}, - {file = "pypiwin32-223.tar.gz", hash = "sha256:71be40c1fbd28594214ecaecb58e7aa8b708eabfa0125c8a109ebd51edbd776a"}, -] python-telegram = [ {file = "python-telegram-0.15.0.tar.gz", hash = "sha256:35ad8d22256061ec4a6c4aa3379f217e4831c057424cbed9ec3dd61212f8c170"}, ] pywin32 = [ - {file = "pywin32-302-cp310-cp310-win32.whl", hash = "sha256:251b7a9367355ccd1a4cd69cd8dd24bd57b29ad83edb2957cfa30f7ed9941efa"}, - {file = "pywin32-302-cp310-cp310-win_amd64.whl", hash = "sha256:79cf7e6ddaaf1cd47a9e50cc74b5d770801a9db6594464137b1b86aa91edafcc"}, - {file = "pywin32-302-cp36-cp36m-win32.whl", hash = "sha256:fe21c2fb332d03dac29de070f191bdbf14095167f8f2165fdc57db59b1ecc006"}, - {file = "pywin32-302-cp36-cp36m-win_amd64.whl", hash = "sha256:d3761ab4e8c5c2dbc156e2c9ccf38dd51f936dc77e58deb940ffbc4b82a30528"}, - {file = "pywin32-302-cp37-cp37m-win32.whl", hash = "sha256:48dd4e348f1ee9538dd4440bf201ea8c110ea6d9f3a5010d79452e9fa80480d9"}, - {file = "pywin32-302-cp37-cp37m-win_amd64.whl", hash = "sha256:496df89f10c054c9285cc99f9d509e243f4e14ec8dfc6d78c9f0bf147a893ab1"}, - {file = "pywin32-302-cp38-cp38-win32.whl", hash = "sha256:e372e477d938a49266136bff78279ed14445e00718b6c75543334351bf535259"}, - {file = "pywin32-302-cp38-cp38-win_amd64.whl", hash = "sha256:543552e66936378bd2d673c5a0a3d9903dba0b0a87235ef0c584f058ceef5872"}, - {file = "pywin32-302-cp39-cp39-win32.whl", hash = "sha256:2393c1a40dc4497fd6161b76801b8acd727c5610167762b7c3e9fd058ef4a6ab"}, - {file = "pywin32-302-cp39-cp39-win_amd64.whl", hash = "sha256:af5aea18167a31efcacc9f98a2ca932c6b6a6d91ebe31f007509e293dea12580"}, + {file = "pywin32-303-cp310-cp310-win32.whl", hash = "sha256:6fed4af057039f309263fd3285d7b8042d41507343cd5fa781d98fcc5b90e8bb"}, + {file = "pywin32-303-cp310-cp310-win_amd64.whl", hash = "sha256:51cb52c5ec6709f96c3f26e7795b0bf169ee0d8395b2c1d7eb2c029a5008ed51"}, + {file = "pywin32-303-cp311-cp311-win32.whl", hash = "sha256:d9b5d87ca944eb3aa4cd45516203ead4b37ab06b8b777c54aedc35975dec0dee"}, + {file = "pywin32-303-cp311-cp311-win_amd64.whl", hash = "sha256:fcf44032f5b14fcda86028cdf49b6ebdaea091230eb0a757282aa656e4732439"}, + {file = "pywin32-303-cp36-cp36m-win32.whl", hash = "sha256:aad484d52ec58008ca36bd4ad14a71d7dd0a99db1a4ca71072213f63bf49c7d9"}, + {file = "pywin32-303-cp36-cp36m-win_amd64.whl", hash = "sha256:2a09632916b6bb231ba49983fe989f2f625cea237219530e81a69239cd0c4559"}, + {file = "pywin32-303-cp37-cp37m-win32.whl", hash = "sha256:b1675d82bcf6dbc96363fca747bac8bff6f6e4a447a4287ac652aa4b9adc796e"}, + {file = "pywin32-303-cp37-cp37m-win_amd64.whl", hash = "sha256:c268040769b48a13367221fced6d4232ed52f044ffafeda247bd9d2c6bdc29ca"}, + {file = "pywin32-303-cp38-cp38-win32.whl", hash = "sha256:5f9ec054f5a46a0f4dfd72af2ce1372f3d5a6e4052af20b858aa7df2df7d355b"}, + {file = "pywin32-303-cp38-cp38-win_amd64.whl", hash = "sha256:793bf74fce164bcffd9d57bb13c2c15d56e43c9542a7b9687b4fccf8f8a41aba"}, + {file = "pywin32-303-cp39-cp39-win32.whl", hash = "sha256:7d3271c98434617a11921c5ccf74615794d97b079e22ed7773790822735cc352"}, + {file = "pywin32-303-cp39-cp39-win_amd64.whl", hash = "sha256:79cbb862c11b9af19bcb682891c1b91942ec2ff7de8151e2aea2e175899cda34"}, ] regex = [ {file = "regex-2021.11.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9345b6f7ee578bad8e475129ed40123d265464c4cfead6c261fd60fc9de00bcf"}, diff --git a/pyproject.toml b/pyproject.toml index 924ac1f..10bae52 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ repository = "https://github.com/TruncatedDinosour/arigram" [tool.poetry.dependencies] python = "^3.8" -python-telegram = "^0.15.0^" +python-telegram = "0.15.0" pyfzf = "^0.2.2" pyperclip = "^1.8.2" diff --git a/requirements.txt b/requirements.txt index 85a8240..9f0a45a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -python-telegram>=0.15.0 +python-telegram==0.15.0 pyfzf>=0.2.2 pyperclip>=1.8.2 diff --git a/setup.py b/setup.py index 7106dce..13ddbe9 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ setup( entry_points={"console_scripts": ["arigram = arigram.__main__:main"]}, python_requires=">=3.8", install_requires=[ - "python-telegram>=0.15.0", + "python-telegram==0.15.0", "pyfzf>=0.2.2", "pyperclip>=1.8.2", ],