Remove Python code
This commit is contained in:
638
Cargo.lock
generated
638
Cargo.lock
generated
@@ -1,638 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ahash"
|
|
||||||
version = "0.8.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"once_cell",
|
|
||||||
"version_check",
|
|
||||||
"zerocopy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aho-corasick"
|
|
||||||
version = "1.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "allocator-api2"
|
|
||||||
version = "0.2.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "antidrift"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"ratatui",
|
|
||||||
"regex",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anyhow"
|
|
||||||
version = "1.0.86"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "2.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cassowary"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "castaway"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
|
|
||||||
dependencies = [
|
|
||||||
"rustversion",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "compact_str"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
|
|
||||||
dependencies = [
|
|
||||||
"castaway",
|
|
||||||
"cfg-if",
|
|
||||||
"itoa",
|
|
||||||
"ryu",
|
|
||||||
"static_assertions",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossterm"
|
|
||||||
version = "0.27.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"crossterm_winapi",
|
|
||||||
"libc",
|
|
||||||
"mio",
|
|
||||||
"parking_lot",
|
|
||||||
"signal-hook",
|
|
||||||
"signal-hook-mio",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossterm_winapi"
|
|
||||||
version = "0.9.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
|
||||||
dependencies = [
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "either"
|
|
||||||
version = "1.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.14.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
|
||||||
dependencies = [
|
|
||||||
"ahash",
|
|
||||||
"allocator-api2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "heck"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itoa"
|
|
||||||
version = "1.0.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.155"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lock_api"
|
|
||||||
version = "0.4.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"scopeguard",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "log"
|
|
||||||
version = "0.4.22"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lru"
|
|
||||||
version = "0.12.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
|
|
||||||
dependencies = [
|
|
||||||
"hashbrown",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memchr"
|
|
||||||
version = "2.7.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mio"
|
|
||||||
version = "0.8.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"wasi",
|
|
||||||
"windows-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.19.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parking_lot"
|
|
||||||
version = "0.12.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
|
||||||
dependencies = [
|
|
||||||
"lock_api",
|
|
||||||
"parking_lot_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parking_lot_core"
|
|
||||||
version = "0.9.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"redox_syscall",
|
|
||||||
"smallvec",
|
|
||||||
"windows-targets 0.52.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "paste"
|
|
||||||
version = "1.0.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.86"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.36"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ratatui"
|
|
||||||
version = "0.27.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"cassowary",
|
|
||||||
"compact_str",
|
|
||||||
"crossterm",
|
|
||||||
"itertools 0.13.0",
|
|
||||||
"lru",
|
|
||||||
"paste",
|
|
||||||
"stability",
|
|
||||||
"strum",
|
|
||||||
"strum_macros",
|
|
||||||
"unicode-segmentation",
|
|
||||||
"unicode-truncate",
|
|
||||||
"unicode-width",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex"
|
|
||||||
version = "1.10.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-automata",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-automata"
|
|
||||||
version = "0.4.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-syntax"
|
|
||||||
version = "0.8.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustversion"
|
|
||||||
version = "1.0.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ryu"
|
|
||||||
version = "1.0.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scopeguard"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "signal-hook"
|
|
||||||
version = "0.3.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"signal-hook-registry",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "signal-hook-mio"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"mio",
|
|
||||||
"signal-hook",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "signal-hook-registry"
|
|
||||||
version = "1.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "smallvec"
|
|
||||||
version = "1.13.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "stability"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "static_assertions"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strum"
|
|
||||||
version = "0.26.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
|
||||||
dependencies = [
|
|
||||||
"strum_macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strum_macros"
|
|
||||||
version = "0.26.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
|
||||||
dependencies = [
|
|
||||||
"heck",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"rustversion",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.68"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-segmentation"
|
|
||||||
version = "1.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-truncate"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226"
|
|
||||||
dependencies = [
|
|
||||||
"itertools 0.12.1",
|
|
||||||
"unicode-width",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-width"
|
|
||||||
version = "0.1.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "version_check"
|
|
||||||
version = "0.9.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasi"
|
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi"
|
|
||||||
version = "0.3.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
|
||||||
dependencies = [
|
|
||||||
"winapi-i686-pc-windows-gnu",
|
|
||||||
"winapi-x86_64-pc-windows-gnu",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-i686-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.48.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
|
||||||
dependencies = [
|
|
||||||
"windows_aarch64_gnullvm 0.48.5",
|
|
||||||
"windows_aarch64_msvc 0.48.5",
|
|
||||||
"windows_i686_gnu 0.48.5",
|
|
||||||
"windows_i686_msvc 0.48.5",
|
|
||||||
"windows_x86_64_gnu 0.48.5",
|
|
||||||
"windows_x86_64_gnullvm 0.48.5",
|
|
||||||
"windows_x86_64_msvc 0.48.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.52.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
|
||||||
dependencies = [
|
|
||||||
"windows_aarch64_gnullvm 0.52.5",
|
|
||||||
"windows_aarch64_msvc 0.52.5",
|
|
||||||
"windows_i686_gnu 0.52.5",
|
|
||||||
"windows_i686_gnullvm",
|
|
||||||
"windows_i686_msvc 0.52.5",
|
|
||||||
"windows_x86_64_gnu 0.52.5",
|
|
||||||
"windows_x86_64_gnullvm 0.52.5",
|
|
||||||
"windows_x86_64_msvc 0.52.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.52.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.52.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.52.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnullvm"
|
|
||||||
version = "0.52.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_msvc"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_msvc"
|
|
||||||
version = "0.52.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.52.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.52.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.52.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy"
|
|
||||||
version = "0.7.34"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
|
|
||||||
dependencies = [
|
|
||||||
"zerocopy-derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy-derive"
|
|
||||||
version = "0.7.34"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
60
README.md
60
README.md
@@ -1,60 +0,0 @@
|
|||||||
# AntiDrift
|
|
||||||
|
|
||||||
Utilize your computer purposefully.
|
|
||||||
|
|
||||||
## Make executable and install
|
|
||||||
|
|
||||||
```
|
|
||||||
pip install pyinstaller --user
|
|
||||||
pyinstaller --onefile antidrift.py
|
|
||||||
sudo cp dist/antidrift /usr/bin
|
|
||||||
```
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- dbus-python
|
|
||||||
- glib
|
|
||||||
|
|
||||||
## Create sudoers configuration
|
|
||||||
|
|
||||||
Create a file `antidrift` in `/etc/sudoers.d`. This allows antidrift to run
|
|
||||||
itself with sudo ultimately making it unkillable from the regular user.
|
|
||||||
|
|
||||||
```
|
|
||||||
user hostname = (root) NOPASSWD: /usr/bin/antidrift
|
|
||||||
```
|
|
||||||
|
|
||||||
## Autostart with systemd
|
|
||||||
|
|
||||||
- Create `~/.config/systemd/user/antidrift.service`
|
|
||||||
- Add configuration below and save
|
|
||||||
- Run `systemctl --user enable antidrift.service`
|
|
||||||
|
|
||||||
```
|
|
||||||
[Unit]
|
|
||||||
Description=AntiDrift
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
ExecStart=antidrift
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=default.target
|
|
||||||
```
|
|
||||||
|
|
||||||
## Autostart via desktop file
|
|
||||||
|
|
||||||
Create a file `antidrift.desktop` in `/etc/xdg/autostart`.
|
|
||||||
|
|
||||||
Add the following content to the file.
|
|
||||||
|
|
||||||
```
|
|
||||||
[Desktop Entry]
|
|
||||||
Name=AntiDrift
|
|
||||||
Exec=antidrift
|
|
||||||
Terminal=false
|
|
||||||
Type=Application
|
|
||||||
StartupNotify=false
|
|
||||||
```
|
|
||||||
|
|
||||||
Your window manager will now start antidrift automatically.
|
|
||||||
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"folders": [
|
|
||||||
{
|
|
||||||
"path": "."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"launch": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Python: Current File",
|
|
||||||
"type": "python",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "${file}",
|
|
||||||
"console": "integratedTerminal"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "FocusFriend Debug",
|
|
||||||
"type": "python",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "focusfriend.py",
|
|
||||||
"console": "integratedTerminal",
|
|
||||||
"args": ["--debug"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"settings": {
|
|
||||||
"python.linting.pylintEnabled": true,
|
|
||||||
"python.linting.enabled": true,
|
|
||||||
"python.linting.pylintArgs": ["--errors-only"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
""" AntiDrift module init """
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
from dbus_next.auth import Authenticator, _AuthResponse
|
|
||||||
|
|
||||||
|
|
||||||
class AuthExternal(Authenticator):
|
|
||||||
"""An authenticator class for the external auth protocol for use with the
|
|
||||||
:class:`MessageBus <dbus_next.message_bus.BaseMessageBus>`.
|
|
||||||
:sealso: https://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, user_uid):
|
|
||||||
self.user_uid = user_uid
|
|
||||||
self.negotiate_unix_fd = False
|
|
||||||
self.negotiating_fds = False
|
|
||||||
|
|
||||||
def _authentication_start(self, negotiate_unix_fd=False) -> str:
|
|
||||||
self.negotiate_unix_fd = negotiate_unix_fd
|
|
||||||
hex_uid = str(self.user_uid).encode().hex()
|
|
||||||
return f"AUTH EXTERNAL {hex_uid}"
|
|
||||||
|
|
||||||
def _receive_line(self, line: str):
|
|
||||||
response, args = _AuthResponse.parse(line)
|
|
||||||
|
|
||||||
if response is _AuthResponse.OK:
|
|
||||||
if self.negotiate_unix_fd:
|
|
||||||
self.negotiating_fds = True
|
|
||||||
return "NEGOTIATE_UNIX_FD"
|
|
||||||
else:
|
|
||||||
return "BEGIN"
|
|
||||||
|
|
||||||
if response is _AuthResponse.AGREE_UNIX_FD:
|
|
||||||
return "BEGIN"
|
|
||||||
|
|
||||||
raise AuthError(f"authentication failed: {response.value}: {args}")
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
import time
|
|
||||||
from dbus_next.aio import MessageBus
|
|
||||||
from dbus_next import BusType
|
|
||||||
from dbus_next.errors import DBusError
|
|
||||||
from antidrift.config import Config
|
|
||||||
from antidrift.evaluate import evaluate
|
|
||||||
from argparse import Namespace
|
|
||||||
from rich import print
|
|
||||||
from antidrift.daemon import IFACE, OPATH, BUS_NAME
|
|
||||||
|
|
||||||
|
|
||||||
async def get_dbus_interface():
|
|
||||||
try:
|
|
||||||
bus = await MessageBus(bus_type=BusType.SESSION).connect()
|
|
||||||
introspection = await bus.introspect(BUS_NAME, OPATH)
|
|
||||||
proxy_obj = bus.get_proxy_object(BUS_NAME, OPATH, introspection)
|
|
||||||
return proxy_obj.get_interface(IFACE)
|
|
||||||
except DBusError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
async def run(args: Namespace, config: Config):
|
|
||||||
if args.evaluate:
|
|
||||||
evaluate(config)
|
|
||||||
return
|
|
||||||
|
|
||||||
interface = await get_dbus_interface()
|
|
||||||
reply = "🟡 ad daemon active but no command"
|
|
||||||
if interface is None:
|
|
||||||
reply = "🔴 ad inactive"
|
|
||||||
elif args.start:
|
|
||||||
reply = await interface.call_start(args.start)
|
|
||||||
elif args.stop:
|
|
||||||
reply = await interface.call_stop()
|
|
||||||
elif args.pause:
|
|
||||||
reply = await interface.call_pause()
|
|
||||||
elif args.block is not None:
|
|
||||||
reply = await interface.call_block(args.block)
|
|
||||||
elif args.intention is not None:
|
|
||||||
reply = await interface.call_intention(args.intention)
|
|
||||||
elif args.unpause:
|
|
||||||
reply = await interface.call_unpause()
|
|
||||||
elif args.schedule:
|
|
||||||
reply = await interface.call_schedule(args.schedule)
|
|
||||||
elif args.tailf:
|
|
||||||
tailf(config)
|
|
||||||
elif args.status:
|
|
||||||
reply = await interface.call_status()
|
|
||||||
print(reply)
|
|
||||||
|
|
||||||
|
|
||||||
def tailf(config):
|
|
||||||
with open(config.daemon_log_file, "r") as f:
|
|
||||||
f.seek(0, 2)
|
|
||||||
while True:
|
|
||||||
line = f.readline()
|
|
||||||
if not line:
|
|
||||||
time.sleep(0.1)
|
|
||||||
else:
|
|
||||||
print(line.strip())
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
import os
|
|
||||||
import yaml
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import List
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
|
|
||||||
class Block(BaseModel):
|
|
||||||
name: str
|
|
||||||
keywords: List[str]
|
|
||||||
kill: bool = False
|
|
||||||
delay: int = 0
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
extra = "forbid"
|
|
||||||
|
|
||||||
|
|
||||||
class Config(BaseModel):
|
|
||||||
blackblocks: List[Block]
|
|
||||||
whiteblocks: List[Block]
|
|
||||||
window_log_file: Path = Path("~/tmp/antidrift/history.log")
|
|
||||||
daemon_log_file: Path = Path()
|
|
||||||
client_log_file: Path = Path()
|
|
||||||
config_file: Path = Path()
|
|
||||||
polling_cycle_ms: int = 2000
|
|
||||||
enforce_delay_ms: int = 5000
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
extra = "forbid"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load(cls, config_file: str) -> Config:
|
|
||||||
config_file = os.path.expanduser(config_file)
|
|
||||||
with open(config_file, "r") as f:
|
|
||||||
config_dict = yaml.safe_load(f)
|
|
||||||
config = cls(**config_dict)
|
|
||||||
config.config_file = Path(config_file)
|
|
||||||
# Expand the paths for the log files
|
|
||||||
config.window_log_file = Path(
|
|
||||||
os.path.expanduser(config.window_log_file))
|
|
||||||
config.daemon_log_file = Path(
|
|
||||||
os.path.expanduser(config.daemon_log_file))
|
|
||||||
config.client_log_file = Path(
|
|
||||||
os.path.expanduser(config.client_log_file))
|
|
||||||
return config
|
|
||||||
|
|
||||||
def save(self) -> None:
|
|
||||||
config_file = self.config_file
|
|
||||||
config_dict = self.dict()
|
|
||||||
|
|
||||||
# convert Path objects to strings
|
|
||||||
for key, value in config_dict.items():
|
|
||||||
if isinstance(value, Path):
|
|
||||||
config_dict[key] = str(value)
|
|
||||||
|
|
||||||
with open(config_file, "w") as f:
|
|
||||||
yaml.safe_dump(config_dict, f)
|
|
||||||
|
|
||||||
|
|
||||||
class State(BaseModel):
|
|
||||||
active_blackblocks: List[Block] = []
|
|
||||||
active_whiteblocks: List[Block] = []
|
|
||||||
inactive_blackblocks: List[Block] = []
|
|
||||||
pause: bool = False
|
|
||||||
intention: str = ""
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
extra = "forbid"
|
|
||||||
@@ -1,313 +0,0 @@
|
|||||||
from datetime import datetime
|
|
||||||
import csv
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import pwd
|
|
||||||
import re
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
import antidrift.xwindow as xwindow
|
|
||||||
from antidrift.xwindow import XWindow
|
|
||||||
from antidrift.config import Config, State, Block
|
|
||||||
from antidrift.auth import AuthExternal
|
|
||||||
|
|
||||||
from dbus_next.aio import MessageBus
|
|
||||||
from dbus_next.service import ServiceInterface, method
|
|
||||||
from dbus_next import BusType
|
|
||||||
|
|
||||||
BUS_NAME = "com.antidrift"
|
|
||||||
IFACE = "com.antidrift"
|
|
||||||
OPATH = "/com/antidrift"
|
|
||||||
s = "no pyflakes warning"
|
|
||||||
|
|
||||||
|
|
||||||
class AntiDriftDaemon(ServiceInterface):
|
|
||||||
def __init__(self, config: Config):
|
|
||||||
super().__init__(IFACE)
|
|
||||||
self.config = config
|
|
||||||
self.reset_block_state()
|
|
||||||
self.enforce_count = 0
|
|
||||||
self.enforce_value = int(
|
|
||||||
config.enforce_delay_ms / config.polling_cycle_ms)
|
|
||||||
|
|
||||||
async def init_bus(self):
|
|
||||||
"""
|
|
||||||
We are switching the effective UID to the target user's UID in order to
|
|
||||||
connect to the D-Bus session bus with the correct permissions and
|
|
||||||
authentication. Once the D-Bus connection is established, we restore
|
|
||||||
the original effective UID to maintain the appropriate privilege
|
|
||||||
levels.
|
|
||||||
"""
|
|
||||||
user_name = os.environ.get(
|
|
||||||
"SUDO_USER", pwd.getpwuid(os.getuid()).pw_name)
|
|
||||||
user_uid = pwd.getpwnam(user_name).pw_uid
|
|
||||||
euid = os.geteuid()
|
|
||||||
os.seteuid(user_uid)
|
|
||||||
auth = AuthExternal(user_uid)
|
|
||||||
bus_address = f"unix:path=/run/user/{user_uid}/bus"
|
|
||||||
bus = MessageBus(bus_address=bus_address,
|
|
||||||
bus_type=BusType.SESSION, auth=auth)
|
|
||||||
await bus.connect()
|
|
||||||
bus.export(OPATH, self)
|
|
||||||
await bus.request_name(BUS_NAME)
|
|
||||||
os.seteuid(euid)
|
|
||||||
return bus
|
|
||||||
|
|
||||||
async def run(self, debug: bool = False):
|
|
||||||
_ = await self.init_bus()
|
|
||||||
|
|
||||||
async def _enforce():
|
|
||||||
while True:
|
|
||||||
if self.state.pause is True:
|
|
||||||
await self.enforce_pause()
|
|
||||||
else:
|
|
||||||
await self.enforce()
|
|
||||||
await asyncio.sleep(self.config.polling_cycle_ms / 1000)
|
|
||||||
|
|
||||||
async def _log():
|
|
||||||
while True:
|
|
||||||
if self.state.pause is False:
|
|
||||||
self.log_window()
|
|
||||||
await asyncio.sleep(60) # Sleep for 60 seconds
|
|
||||||
|
|
||||||
# Start _enforce and _log as background tasks
|
|
||||||
asyncio.create_task(_enforce())
|
|
||||||
asyncio.create_task(_log())
|
|
||||||
|
|
||||||
xwindow.notify("✅ Antidrift running.")
|
|
||||||
stop = asyncio.Event()
|
|
||||||
await stop.wait()
|
|
||||||
|
|
||||||
def reset_block_state(self):
|
|
||||||
self.state = State(
|
|
||||||
active_blackblocks=self.config.blackblocks,
|
|
||||||
active_whiteblocks=[],
|
|
||||||
inactive_blackblocks=[],
|
|
||||||
)
|
|
||||||
|
|
||||||
@method()
|
|
||||||
def start(self, whiteblocks: "as") -> "s":
|
|
||||||
self.reset_block_state()
|
|
||||||
all_whiteblocks = {wb.name: wb for wb in self.config.whiteblocks}
|
|
||||||
success_wbs, fail_blocks = [], []
|
|
||||||
for block_name in whiteblocks:
|
|
||||||
if block_name in all_whiteblocks:
|
|
||||||
self.state.active_whiteblocks.append(
|
|
||||||
all_whiteblocks[block_name])
|
|
||||||
success_wbs.append(block_name)
|
|
||||||
else:
|
|
||||||
fail_blocks.append(block_name)
|
|
||||||
if success_wbs:
|
|
||||||
wbs = ", ".join(success_wbs)
|
|
||||||
r = f"Start whiteblocks [sky_blue3]{wbs}[/sky_blue3]."
|
|
||||||
logging.info(r)
|
|
||||||
else:
|
|
||||||
r = "No whiteblocks started."
|
|
||||||
if fail_blocks:
|
|
||||||
m = f"No whiteblocks [red3]{', '.join(fail_blocks)}[/red3]."
|
|
||||||
logging.warning(m)
|
|
||||||
return r
|
|
||||||
|
|
||||||
@method()
|
|
||||||
def schedule(self, blackblock_name: "s") -> "s":
|
|
||||||
"""Schedule blackblock based if it has a non-zero timeout value."""
|
|
||||||
all_blackblocks = {bb.name: bb for bb in self.config.blackblocks}
|
|
||||||
if blackblock_name not in all_blackblocks:
|
|
||||||
m = f"No blackblock [red3]{blackblock_name}[/red3]."
|
|
||||||
logging.warning(m)
|
|
||||||
return m
|
|
||||||
|
|
||||||
blackblock = all_blackblocks[blackblock_name]
|
|
||||||
if blackblock.delay == 0:
|
|
||||||
m = f"Blackblock [red3]{blackblock_name}[/red3] cannot be scheduled without delay."
|
|
||||||
logging.warning(m)
|
|
||||||
return m
|
|
||||||
|
|
||||||
def allow():
|
|
||||||
self.allow_blackblock(blackblock)
|
|
||||||
|
|
||||||
delay_sec = blackblock.delay * 60
|
|
||||||
loop = asyncio.get_event_loop()
|
|
||||||
loop.call_later(delay_sec, allow)
|
|
||||||
m = f"Scheduled [sky_blue3]{blackblock.name}[/sky_blue3] in {blackblock.delay} minutes."
|
|
||||||
logging.info(m)
|
|
||||||
return m
|
|
||||||
|
|
||||||
@method()
|
|
||||||
def stop(self) -> "s":
|
|
||||||
self.reset_block_state()
|
|
||||||
m = "Blacklist only mode."
|
|
||||||
logging.info(m)
|
|
||||||
return m
|
|
||||||
|
|
||||||
@method()
|
|
||||||
def pause(self) -> "s":
|
|
||||||
self.state.pause = True
|
|
||||||
m = "Antidrift paused."
|
|
||||||
logging.info(m)
|
|
||||||
return m
|
|
||||||
|
|
||||||
@method()
|
|
||||||
def unpause(self) -> "s":
|
|
||||||
self.state.pause = False
|
|
||||||
m = "Antidrift unpaused."
|
|
||||||
logging.info(m)
|
|
||||||
return m
|
|
||||||
|
|
||||||
@method()
|
|
||||||
def intention(self, intention: "s") -> "s":
|
|
||||||
self.state.intention = intention
|
|
||||||
m = f"Antidrift intention set to '{intention}'."
|
|
||||||
logging.info(m)
|
|
||||||
return m
|
|
||||||
|
|
||||||
@method()
|
|
||||||
def block(self, block: "s") -> "s":
|
|
||||||
# self.state.intention = intention
|
|
||||||
if self.state.active_blackblocks and \
|
|
||||||
block not in self.state.active_blackblocks[0].keywords:
|
|
||||||
self.state.active_blackblocks[0].keywords.append(block)
|
|
||||||
self.config.save()
|
|
||||||
m = f"✅Antidrift add block '{block}'."
|
|
||||||
logging.info(m)
|
|
||||||
return m
|
|
||||||
return f"⚠️ '{block}' not added."
|
|
||||||
|
|
||||||
@method()
|
|
||||||
def status(self) -> "s":
|
|
||||||
white_active = bool(self.state.active_whiteblocks)
|
|
||||||
black_active = bool(self.state.active_blackblocks)
|
|
||||||
m = "🟢 ad "
|
|
||||||
inactive_bbs = " ".join(
|
|
||||||
map(lambda b: "-" + b.name, self.state.inactive_blackblocks)
|
|
||||||
)
|
|
||||||
if self.state.pause is True:
|
|
||||||
return "🟡 ad paused"
|
|
||||||
match (white_active, black_active):
|
|
||||||
case (True, _):
|
|
||||||
m += "wb: "
|
|
||||||
m += " ".join(map(lambda b: b.name,
|
|
||||||
self.state.active_whiteblocks))
|
|
||||||
if inactive_bbs:
|
|
||||||
m += " "
|
|
||||||
m += inactive_bbs
|
|
||||||
case (False, True):
|
|
||||||
m += "bb"
|
|
||||||
if inactive_bbs:
|
|
||||||
m += ": "
|
|
||||||
m += inactive_bbs
|
|
||||||
case _:
|
|
||||||
m = "inactive"
|
|
||||||
if self.state.intention:
|
|
||||||
m += f" 🎯 {self.state.intention}"
|
|
||||||
return m
|
|
||||||
|
|
||||||
def allow_blackblock(self, blackblock: Block):
|
|
||||||
if blackblock in self.state.active_blackblocks:
|
|
||||||
self.state.active_blackblocks.remove(blackblock)
|
|
||||||
if blackblock not in self.state.inactive_blackblocks:
|
|
||||||
self.state.inactive_blackblocks.append(blackblock)
|
|
||||||
m = f"Blackblock [sky_blue3]{blackblock.name}[/sky_blue3] is now allowed."
|
|
||||||
logging.info(m)
|
|
||||||
|
|
||||||
def log_window(self):
|
|
||||||
window = XWindow()
|
|
||||||
utc_timestamp = datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
|
|
||||||
|
|
||||||
# Remove time strings and numbers with decimal from window.name
|
|
||||||
window_name = re.sub(r"\b\d\d:\d\d\b", "", window.name)
|
|
||||||
window_name = re.sub(r"\b\d+.\d\d\b", "", window_name)
|
|
||||||
window_name = re.sub(r"[+-]\d+.\d+%", "", window_name)
|
|
||||||
|
|
||||||
intention = self.state.intention
|
|
||||||
log_line = [utc_timestamp, window_name, window.cls, intention]
|
|
||||||
with self.config.window_log_file.open("a", newline="") as f:
|
|
||||||
writer = csv.writer(f)
|
|
||||||
writer.writerow(log_line)
|
|
||||||
|
|
||||||
async def enforce_pause(self):
|
|
||||||
if XWindow().is_active() is False:
|
|
||||||
return
|
|
||||||
xwindow.notify("⚠️ No active windows during pause!")
|
|
||||||
for _ in range(8):
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
window = XWindow()
|
|
||||||
if not window.is_active() or self.state.pause is False:
|
|
||||||
xwindow.notify("✅ Thanks.")
|
|
||||||
return
|
|
||||||
|
|
||||||
window = XWindow()
|
|
||||||
if window.is_active():
|
|
||||||
window.minimize()
|
|
||||||
|
|
||||||
async def enforce(self):
|
|
||||||
if not window_is_blocked(self.state):
|
|
||||||
return
|
|
||||||
|
|
||||||
delay = int(self.config.enforce_delay_ms / 1000)
|
|
||||||
for i in range(delay, 0, -1):
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
xwindow.notify(f"⚠️ AntiDrift will minimize in {i}s.")
|
|
||||||
if not window_is_blocked(self.state, silent=True):
|
|
||||||
xwindow.notify("✅ We are gucci again.")
|
|
||||||
return
|
|
||||||
window = XWindow()
|
|
||||||
xwindow.notify(f"⛔ Minimize {window.name[:30]}.")
|
|
||||||
window.minimize()
|
|
||||||
|
|
||||||
|
|
||||||
def window_is_blocked(state: State, silent: bool = False) -> bool:
|
|
||||||
blackblocks = state.active_blackblocks
|
|
||||||
whiteblocks = state.active_whiteblocks
|
|
||||||
|
|
||||||
window = XWindow()
|
|
||||||
if not window.keywords:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def keyword_matches_window(keyword: str, window: XWindow):
|
|
||||||
if keyword.startswith("/") and keyword.endswith("/"):
|
|
||||||
try:
|
|
||||||
r = re.compile(keyword[1:-1], re.IGNORECASE)
|
|
||||||
if r.findall(window.name):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
except re.error:
|
|
||||||
m = f"Invalid regex [red3]{keyword}[/red3]."
|
|
||||||
logging.warning(m)
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
if k in window.keywords:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
for b in blackblocks:
|
|
||||||
for k in b.keywords:
|
|
||||||
if keyword_matches_window(k, window) and b.kill:
|
|
||||||
window.kill()
|
|
||||||
xwindow.notify(f"⛔ Kill for {k} on {b.name}.")
|
|
||||||
return True
|
|
||||||
elif keyword_matches_window(k, window):
|
|
||||||
if not silent:
|
|
||||||
xwindow.notify(f"{window.name[:30]} blocked by {b.name}.")
|
|
||||||
logging.warning(
|
|
||||||
f"[red]{window.name[:50]}[/red] "
|
|
||||||
f"blocked by [red]{b.name}[/red]."
|
|
||||||
)
|
|
||||||
return True
|
|
||||||
if not whiteblocks:
|
|
||||||
if not silent:
|
|
||||||
logging.debug("ℹ️ All non-blackblock windows are allowed.")
|
|
||||||
return False
|
|
||||||
for w in whiteblocks:
|
|
||||||
for k in w.keywords:
|
|
||||||
if keyword_matches_window(k, window):
|
|
||||||
if not silent:
|
|
||||||
logging.debug(
|
|
||||||
f"[pale_green3]{window.name[:30]}[/pale_green3] "
|
|
||||||
f"allowed by [sky_blue3]{w.name}[/sky_blue3]."
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
if not silent:
|
|
||||||
xwindow.notify(f"'{window.name[:30]}…' not on any whiteblock.")
|
|
||||||
return True
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
import csv
|
|
||||||
import keyring
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
import antidrift.xwindow as xwindow
|
|
||||||
from antidrift.config import Config
|
|
||||||
from collections import defaultdict
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from functools import lru_cache
|
|
||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Datapoint:
|
|
||||||
timestamp: datetime
|
|
||||||
title: str
|
|
||||||
tool: str
|
|
||||||
intention: str
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Evaluation:
|
|
||||||
level: str
|
|
||||||
reason: str
|
|
||||||
|
|
||||||
|
|
||||||
def filter_today(datapoints: List[Datapoint]) -> List[Datapoint]:
|
|
||||||
today = datetime.now().date()
|
|
||||||
return [d for d in datapoints if d.timestamp.date() == today]
|
|
||||||
|
|
||||||
|
|
||||||
def filter_last_hour(datapoints: List[Datapoint]) -> List[Datapoint]:
|
|
||||||
one_hour_ago = datetime.now() - timedelta(minutes=50)
|
|
||||||
return [d for d in datapoints if d.timestamp >= one_hour_ago]
|
|
||||||
|
|
||||||
|
|
||||||
def evaluate(config: Config):
|
|
||||||
log_file = config.window_log_file
|
|
||||||
datapoints: List[Datapoint] = []
|
|
||||||
|
|
||||||
with open(log_file, "r") as file:
|
|
||||||
reader = csv.reader(file)
|
|
||||||
for row in reader:
|
|
||||||
timestamp_str, title, tool, intention = row
|
|
||||||
if title != "":
|
|
||||||
timestamp = datetime.fromisoformat(timestamp_str)
|
|
||||||
datapoint = Datapoint(timestamp, title, tool, intention)
|
|
||||||
datapoints.append(datapoint)
|
|
||||||
|
|
||||||
datapoints = filter_last_hour(datapoints)
|
|
||||||
durations = defaultdict(timedelta)
|
|
||||||
prev_datapoint = None
|
|
||||||
prev_evaluation = None
|
|
||||||
for d in datapoints:
|
|
||||||
if d.title == "":
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Get evaluation of current datapoint
|
|
||||||
result = evaluate_datapoint(d.title, d.tool, d.intention)
|
|
||||||
evaluation = parse_result(result)
|
|
||||||
|
|
||||||
# If there was a previous datapoint and evaluation
|
|
||||||
if prev_datapoint and prev_evaluation:
|
|
||||||
# Calculate time difference between current and previous datapoint
|
|
||||||
time_diff = d.timestamp - prev_datapoint.timestamp
|
|
||||||
# Add this time difference to the corresponding level's duration
|
|
||||||
durations[prev_evaluation.level] += time_diff
|
|
||||||
|
|
||||||
# Update previous datapoint and evaluation
|
|
||||||
prev_datapoint = d
|
|
||||||
prev_evaluation = evaluation
|
|
||||||
|
|
||||||
# Print durations for each level
|
|
||||||
for level, duration in durations.items():
|
|
||||||
print(f"Level: {level}, Duration: {duration}")
|
|
||||||
|
|
||||||
|
|
||||||
def parse_result(result: str) -> Optional[Evaluation]:
|
|
||||||
try:
|
|
||||||
content = json.loads(result.strip())
|
|
||||||
return Evaluation(content["level"], content["reason"])
|
|
||||||
except (ValueError, KeyError):
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
@lru_cache
|
|
||||||
def evaluate_datapoint(title, tool, intention) -> Optional[str]:
|
|
||||||
messages = []
|
|
||||||
api_key = keyring.get_password("openai-api-key", "felixm")
|
|
||||||
prompt = get_prompt(title, tool, intention)
|
|
||||||
|
|
||||||
instruction = "You are productivity rater GPT and classify work sessions."
|
|
||||||
messages.append({"role": "system", "content": instruction})
|
|
||||||
messages.append({"role": "user", "content": prompt})
|
|
||||||
headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Authorization": f"Bearer {api_key}",
|
|
||||||
}
|
|
||||||
|
|
||||||
BASE_ENDPOINT = "https://api.openai.com/v1"
|
|
||||||
body = {"model": "gpt-4", "messages": messages}
|
|
||||||
|
|
||||||
try:
|
|
||||||
r = requests.post(
|
|
||||||
f"{BASE_ENDPOINT}/chat/completions", headers=headers, json=body, timeout=10
|
|
||||||
)
|
|
||||||
except requests.ConnectionError:
|
|
||||||
xwindow.notify("Antidrift - GPT - Connection error")
|
|
||||||
return None
|
|
||||||
except requests.Timeout:
|
|
||||||
xwindow.notify("Antidrift - GPT - Timeout")
|
|
||||||
return None
|
|
||||||
|
|
||||||
if r.status_code == 200:
|
|
||||||
response = r.json()
|
|
||||||
message_response = response["choices"][0]["message"]
|
|
||||||
return message_response["content"]
|
|
||||||
else:
|
|
||||||
xwindow.notify(f"Antidrift - GPT - Response error status code {r.status_code}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_prompt(title: str, tool: str, intention: str) -> str:
|
|
||||||
return f"""
|
|
||||||
Rate how well that title and tool matches the intention.
|
|
||||||
|
|
||||||
Use one of the following levels:
|
|
||||||
|
|
||||||
deep work, shallow work, good media, bad media, inappropriate
|
|
||||||
|
|
||||||
Return your response as JSON object with the attributes 'level' and 'reason'.
|
|
||||||
|
|
||||||
Adult or other inappropriate NSFW content always scores 'inappropriate'.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
Intention: Work on coding.
|
|
||||||
Tool: VS Code
|
|
||||||
Title: main.py - GoalGuard - Code - OSS
|
|
||||||
|
|
||||||
Response:
|
|
||||||
{{
|
|
||||||
"level": "deep work",
|
|
||||||
"reason": "The user uses VS code to work on coding."
|
|
||||||
}}
|
|
||||||
|
|
||||||
Intention: Watch educational video.
|
|
||||||
Tool: Firefox
|
|
||||||
Title: World's hardest jigsaw puzzle - YouTube - Mozilla Firefox
|
|
||||||
|
|
||||||
Response:
|
|
||||||
{{
|
|
||||||
"level": "good media",
|
|
||||||
"reason": "The user does the desired activity, and it seems educational."
|
|
||||||
}}
|
|
||||||
|
|
||||||
Intention: no intention
|
|
||||||
Tool: Firefox
|
|
||||||
Title: Reddit - Mozilla Firefox
|
|
||||||
|
|
||||||
Response:
|
|
||||||
{{
|
|
||||||
"level": "bad media",
|
|
||||||
"reason": "The user does not have an intention and wastes time on reddit."
|
|
||||||
}}
|
|
||||||
|
|
||||||
Intention: Watch educational video.
|
|
||||||
Tool: Firefox
|
|
||||||
Title: 8tube.com - Mozilla Firefox Private Browsing
|
|
||||||
|
|
||||||
Response:
|
|
||||||
{{
|
|
||||||
"level": "inapproriate",
|
|
||||||
"reason": "The user consumes adult content."
|
|
||||||
}}
|
|
||||||
|
|
||||||
Intention: no intention
|
|
||||||
Tool: Firefox
|
|
||||||
Title: Amazing Marvin - Daily Tasks — Mozilla Firefox
|
|
||||||
|
|
||||||
Response:
|
|
||||||
{{
|
|
||||||
"level": "shallow work",
|
|
||||||
"reason": "The user works on their task list but does not engage in deep work."
|
|
||||||
}}
|
|
||||||
|
|
||||||
Intention: {intention}
|
|
||||||
Tool: {tool}
|
|
||||||
Title: {title}
|
|
||||||
|
|
||||||
Response:
|
|
||||||
"""
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
import os
|
|
||||||
import re
|
|
||||||
import pwd
|
|
||||||
import subprocess
|
|
||||||
import logging
|
|
||||||
|
|
||||||
|
|
||||||
class XWindow:
|
|
||||||
def __init__(self):
|
|
||||||
self.window = self._run(["getactivewindow"])
|
|
||||||
if not self.window:
|
|
||||||
self.name = ""
|
|
||||||
self.cls = ""
|
|
||||||
self.pid = ""
|
|
||||||
else:
|
|
||||||
self.name = self._run(["getwindowname", self.window])
|
|
||||||
self.cls = self._run(["getwindowclassname", self.window])
|
|
||||||
self.pid = self._run(["getwindowpid", self.window])
|
|
||||||
self.keywords = list(re.findall(r"\w+", self.name.lower()))
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return (
|
|
||||||
f"<XWindow '{self.name[:20]}' '{self.cls[:20]}' active: {self.is_active()}>"
|
|
||||||
)
|
|
||||||
|
|
||||||
def _run(self, cmd) -> str:
|
|
||||||
cmd = ["xdotool"] + cmd
|
|
||||||
p = subprocess.run(cmd, capture_output=True)
|
|
||||||
if p.returncode != 0:
|
|
||||||
return ""
|
|
||||||
return p.stdout.decode().strip()
|
|
||||||
|
|
||||||
def minimize(self):
|
|
||||||
self._run(["windowminimize", self.window])
|
|
||||||
|
|
||||||
def quit(self):
|
|
||||||
self._run(["windowquit", self.window])
|
|
||||||
|
|
||||||
def kill(self):
|
|
||||||
self._run(["windowkill", self.window])
|
|
||||||
|
|
||||||
def is_active(self) -> bool:
|
|
||||||
current_desktop = self._run(["get_desktop"])
|
|
||||||
window_desktop = self._run(["get_desktop_for_window", self.window])
|
|
||||||
return True if self.name and current_desktop == window_desktop else False
|
|
||||||
|
|
||||||
|
|
||||||
def notify(message: str) -> None:
|
|
||||||
"""Notify user via the Xorg notify-send command."""
|
|
||||||
logging.debug(f"{message} - [grey]notify[/grey]")
|
|
||||||
env = dict(os.environ)
|
|
||||||
user = env.get("SUDO_USER", None)
|
|
||||||
if user is None:
|
|
||||||
cmd = ["notify-send", message]
|
|
||||||
else:
|
|
||||||
uid = pwd.getpwnam(user)[2]
|
|
||||||
env["DBUS_SESSION_BUS_ADDRESS"] = f"unix:path=/run/user/{uid}/bus"
|
|
||||||
cmd = ["runuser", "-m", "-u", user, "notify-send", message]
|
|
||||||
subprocess.run(cmd, env=env)
|
|
||||||
132
main.py
132
main.py
@@ -1,132 +0,0 @@
|
|||||||
import logging
|
|
||||||
import shutil
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import argparse
|
|
||||||
import psutil
|
|
||||||
import asyncio
|
|
||||||
from rich.logging import RichHandler
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import antidrift.client
|
|
||||||
from antidrift.daemon import AntiDriftDaemon
|
|
||||||
from antidrift.config import Config
|
|
||||||
|
|
||||||
|
|
||||||
def get_args():
|
|
||||||
parser = argparse.ArgumentParser(description="AntiDrift CLI.")
|
|
||||||
parser.add_argument("--daemon", action="store_true", help="run daemon")
|
|
||||||
parser.add_argument("--evaluate", action="store_true", help="evaluate day")
|
|
||||||
parser.add_argument(
|
|
||||||
"--intention", metavar="intention", help="set intention", default=None, type=str
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--block", metavar="block", help="add to block", default=None, type=str
|
|
||||||
)
|
|
||||||
parser.add_argument("--pause", action="store_true", help="pause antidrift")
|
|
||||||
parser.add_argument("--schedule", metavar="blackblock", help="schedule blackblock")
|
|
||||||
parser.add_argument(
|
|
||||||
"--start", metavar="whiteblock", nargs="+", help="start whiteblocks"
|
|
||||||
)
|
|
||||||
parser.add_argument("--status", action="store_true", help="get status from daemon")
|
|
||||||
parser.add_argument("--stop", action="store_true", help="stop session")
|
|
||||||
parser.add_argument("--tailf", action="store_true", help="tail -f log file")
|
|
||||||
parser.add_argument("--unpause", action="store_true", help="unpause antidrift")
|
|
||||||
args = parser.parse_args()
|
|
||||||
return args
|
|
||||||
|
|
||||||
|
|
||||||
def init_logging(log_file: Path, dev_mode: bool = False):
|
|
||||||
class DuplicateFilter(logging.Filter):
|
|
||||||
def filter(self, record) -> bool:
|
|
||||||
current_log = (record.module, record.levelno, record.msg)
|
|
||||||
if current_log != getattr(self, "last_log", None):
|
|
||||||
self.last_log = current_log
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
if dev_mode:
|
|
||||||
format_str = "%(message)s" # RichHandler will handle the formatting
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.DEBUG,
|
|
||||||
format=format_str,
|
|
||||||
datefmt="%a %H:%M:%S",
|
|
||||||
handlers=[RichHandler(rich_tracebacks=True, markup=True)],
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
format_str = (
|
|
||||||
"[bold pale_green3]%(asctime)s[/bold pale_green3] | "
|
|
||||||
"[light_steel_blue]%(levelname)-8s[/light_steel_blue] | "
|
|
||||||
"%(message)s"
|
|
||||||
)
|
|
||||||
logging.basicConfig(
|
|
||||||
filename=log_file,
|
|
||||||
format=format_str,
|
|
||||||
datefmt="%a %H:%M:%S",
|
|
||||||
encoding="utf-8",
|
|
||||||
level=logging.DEBUG,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
|
||||||
logger.addFilter(DuplicateFilter())
|
|
||||||
|
|
||||||
|
|
||||||
def check_for_xdotool():
|
|
||||||
"""Check if xdotool is in path and exit if not"""
|
|
||||||
result = shutil.which("xdotool")
|
|
||||||
if not result:
|
|
||||||
logging.critical("Please install xdotool")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def kill_existing_antidrift():
|
|
||||||
current_pid = os.getpid()
|
|
||||||
for proc in psutil.process_iter(["pid", "name", "exe"]):
|
|
||||||
if (
|
|
||||||
proc.info["name"] == "/usr/bin/antidrift"
|
|
||||||
or proc.info["exe"] == "/usr/bin/antidrift"
|
|
||||||
):
|
|
||||||
if proc.info["pid"] == current_pid:
|
|
||||||
continue # don't this process
|
|
||||||
try:
|
|
||||||
proc.kill()
|
|
||||||
except psutil.AccessDenied:
|
|
||||||
pass
|
|
||||||
except psutil.NoSuchProcess:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def main_daemon():
|
|
||||||
if os.geteuid() == 0:
|
|
||||||
newpid = os.fork()
|
|
||||||
if newpid == 0:
|
|
||||||
config = Config.load(os.path.expanduser("~/.config/antidrift.yaml"))
|
|
||||||
init_logging(config.daemon_log_file)
|
|
||||||
daemon = AntiDriftDaemon(config)
|
|
||||||
asyncio.run(daemon.run())
|
|
||||||
else:
|
|
||||||
if sys.argv[0] == "antidrift":
|
|
||||||
kill_existing_antidrift()
|
|
||||||
cmd = ["sudo", "antidrift", "--daemon"]
|
|
||||||
subprocess.Popen(cmd)
|
|
||||||
else:
|
|
||||||
config = Config.load(os.path.expanduser("~/.config/antidrift.yaml"))
|
|
||||||
init_logging(config.daemon_log_file, dev_mode=True)
|
|
||||||
daemon = AntiDriftDaemon(config)
|
|
||||||
asyncio.run(daemon.run(debug=True))
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
"""Main routine that dispatches to client or daemon"""
|
|
||||||
check_for_xdotool()
|
|
||||||
args = get_args()
|
|
||||||
if args.daemon:
|
|
||||||
main_daemon()
|
|
||||||
else:
|
|
||||||
config = Config.load(os.path.expanduser("~/.config/antidrift.yaml"))
|
|
||||||
asyncio.run(antidrift.client.run(args, config))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Reference in New Issue
Block a user