archey4/archey

962 lines
39 KiB
Python
Executable File

#!/usr/bin/env python3
import json
import os
import re
import sys
from enum import Enum
from glob import glob
from math import floor
from os import getenv, getuid
from subprocess import CalledProcessError, DEVNULL, PIPE, Popen, \
TimeoutExpired, check_output
# -------------- Enumerations -------------- #
class Distributions(Enum):
ARCH_LINUX = 'Arch.*'
BUNSENLABS = 'BunsenLabs'
CRUNCHBANG = 'CrunchBang'
DEBIAN = '(Rasp|De)bian'
FEDORA = 'Fedora'
GENTOO = 'Gentoo'
KALI_LINUX = 'Kali'
MANJARO_LINUX = 'ManjaroLinux'
LINUX = 'Linux'
LINUX_MINT = 'LinuxMint'
OPENSUSE = 'openSUSE'
RED_HAT = 'Red Hat'
UBUNTU = 'Ubuntu'
WINDOWS = 'Windows'
# -------------- Dictionaries -------------- #
colorDict = {
Distributions.ARCH_LINUX: ['\x1b[0;34m', '\x1b[1;34m'],
Distributions.BUNSENLABS: ['\x1b[1;37m', '\x1b[1;33m', '\x1b[0;33m'],
Distributions.CRUNCHBANG: ['\x1b[1;37m', '\x1b[1;37m'],
Distributions.DEBIAN: ['\x1b[0;31m', '\x1b[1;31m'],
Distributions.FEDORA: ['\x1b[1;37m', '\x1b[0;34m'],
Distributions.GENTOO: ['\x1b[1;37m', '\x1b[1;35m'],
Distributions.KALI_LINUX: ['\x1b[1;37m', '\x1b[1;34m'],
Distributions.MANJARO_LINUX: ['\x1b[0;32m', '\x1b[1;32m'],
Distributions.LINUX: ['\x1b[1;33m', '\x1b[1;37m'],
Distributions.LINUX_MINT: ['\x1b[1;37m', '\x1b[1;32m'],
Distributions.OPENSUSE: ['\x1b[1;37m', '\x1b[1;32m'],
Distributions.RED_HAT: ['\x1b[1;37m', '\x1b[1;31m', '\x1b[0;31m'],
Distributions.UBUNTU: ['\x1b[0;31m', '\x1b[1;31m', '\x1b[0;33m'],
Distributions.WINDOWS: ['\x1b[1;31m', '\x1b[1;34m',
'\x1b[1;32m', '\x1b[0;33m'],
'sensors': ['\x1b[0;32m', '\x1b[0;33m', '\x1b[0;31m'],
'clear': '\x1b[0m'
}
deDict = {
'cinnamon': 'Cinnamon',
'gnome-session': 'GNOME',
'gnome-shell': 'GNOME',
'mate-session': 'MATE',
'ksmserver': 'KDE',
'xfce4-session': 'Xfce',
'fur-box-session': 'Fur Box',
'lxsession': 'LXDE',
'lxqt-session': 'LXQt'
}
wmDict = {
'awesome': 'Awesome',
'beryl': 'Beryl',
'blackbox': 'Blackbox',
'bspwm': 'bspwm',
'cinnamon': 'Cinnamon',
'compiz': 'Compiz',
'dwm': 'DWM',
'enlightenment': 'Enlightenment',
'herbstluftwm': 'herbstluftwm',
'fluxbox': 'Fluxbox',
'fvwm': 'FVWM',
'i3': 'i3',
'icewm': 'IceWM',
'kwin_x11': 'KWin',
'metacity': 'Metacity',
'musca': 'Musca',
'openbox': 'Openbox',
'pekwm': 'PekWM',
'qtile': 'QTile',
'ratpoison': 'ratpoison',
'scrotwm': 'ScrotWM',
'stumpwm': 'StumpWM',
'subtle': 'Subtle',
'monsterwm': 'MonsterWM',
'wingo': 'Wingo',
'wmaker': 'Window Maker',
'wmfs': 'Wmfs',
'wmii': 'wmii',
'xfwm4': 'Xfwm',
'xmonad': 'xmonad'
}
logosDict = {
Distributions.ARCH_LINUX: """
{c[1]} + {r[0]}
{c[1]} # {r[1]}
{c[1]} ### {r[2]}
{c[1]} ##### {r[3]}
{c[1]} ###### {r[4]}
{c[1]} ; #####; {r[5]}
{c[1]} +##.##### {r[6]}
{c[1]} +########## {r[7]}
{c[1]} ######{c[0]}#####{c[1]}##; {r[8]}
{c[1]} ###{c[0]}############{c[1]}+ {r[9]}
{c[1]} #{c[0]}###### #######{c[1]} {r[10]}
{c[1]} {c[0]}.######; ;###;`\".{c[1]} {r[11]}
{c[1]} {c[0]}.#######; ;#####.{c[1]} {r[12]}
{c[1]} {c[0]}#########. .########`{c[1]} {r[13]}
{c[1]} {c[0]}######' '######{c[1]} {r[14]}
{c[1]} {c[0]};#### ####;{c[1]} {r[15]}
{c[1]} {c[0]}##' '##{c[1]} {r[16]}
{c[1]} {c[0]}#' `#{c[1]} {r[17]}\n""",
Distributions.BUNSENLABS: """
{c[0]} .{c[1]}..{c[0]}+hhy+-` \0
{c[0]} `+hd{c[1]}-{c[0]}+dddd{c[2]}hyso{c[0]}+//: \0
{c[0]} `+dddd{c[1]}:-{c[0]}sdh/. {r[0]}
{c[0]} -hdddddh{c[1]}-.{c[2]}/:{c[0]} {r[1]}
{c[0]} /ddddddddd{c[1]}:```{c[0]} {r[2]}
{c[0]} :ddddddddddd/ {r[3]}
{c[0]} `hdddddddddddd+ {r[4]}
{c[0]} /dddddddddddddd: {r[5]}
{c[0]} odddds..sddddddh {r[6]}
{c[0]} oddd/ /dddddd: {r[7]}
{c[0]} +dd+ +ddddd+ {r[8]}
{c[0]} .dd` `ddddd+ {r[9]}
{c[0]} oh ydddd: {r[10]}
{c[0]} `o sdddh` {r[11]}
{c[0]} yddd: {r[12]}
{c[0]} `dddo {r[13]}
{c[0]} :s :dds {r[14]}
{c[0]} yd/ yd+ {r[15]}
{c[0]} `sddy :h- {r[16]}
{c[0]} `sddys` :` {r[17]}
{c[0]} -hdy+`y+yo./+/:- \0
{c[0]} ... .o++oso+/ \n""",
Distributions.CRUNCHBANG: """
{c[0]} ___ ___ _ {r[0]}
{c[0]} / / / / | | {r[1]}
{c[0]} / / / / | | {r[2]}
{c[0]} / / / / | | {r[3]}
{c[0]} _______/ /______/ /______ | | {r[4]}
{c[0]} /______ _______ _______/ | | {r[5]}
{c[0]} / / / / | | {r[6]}
{c[0]} / / / / | | {r[7]}
{c[0]} / / / / | | {r[8]}
{c[0]} ______/ /______/ /______ | | {r[9]}
{c[0]} /_____ _______ _______/ | | {r[10]}
{c[0]} / / / / | | {r[11]}
{c[0]} / / / / |_| {r[12]}
{c[0]} / / / / _ {r[13]}
{c[0]} / / / / | | {r[14]}
{c[0]} /__/ /__/ |_| {r[15]}
{c[0]} {r[16]}
{c[0]} {r[17]}\n""",
Distributions.DEBIAN: """
{c[1]} {r[0]}
{c[1]} _sudZUZ#Z#XZo=_ {r[1]}
{c[1]} _jmZZ2!!~---~!!X##wx {r[2]}
{c[1]} .<wdP~~ -!YZL, {r[3]}
{c[1]} .mX2' _xaaa__ XZ[. {r[4]}
{c[1]} oZ[ _jdXY!~?S#wa ]Xb; {r[5]}
{c[1]} _#e' .]X2( ~Xw| )XXc {r[6]}
{c[1]} .2Z` ]X[. xY| ]oZ( {r[7]}
{c[1]} .2#; )3k; _s!~ jXf` {r[8]}
{c[1]} {c[0]}1Z>{c[1]} {c[0]}-]Xb/{c[1]} {c[0]}~{c[1]} {c[0]}__#2({c[1]} {r[9]}
{c[1]} {c[0]}-Zo;{c[1]} {c[0]}+!4ZwerfgnZZXY'{c[1]} {r[10]}
{c[1]} {c[0]}*#[,{c[1]} {c[0]}~-?!!!!!!-~{c[1]} {r[11]}
{c[1]} {c[0]}XUb;.{c[1]} {r[12]}
{c[1]} {c[0]})YXL,,{c[1]} {r[13]}
{c[1]} {c[0]}+3#bc,{c[1]} {r[14]}
{c[1]} {c[0]}-)SSL,,{c[1]} {r[15]}
{c[1]} {c[0]}~~~~~{c[1]} {r[16]}
{c[1]} {r[17]}\n""",
Distributions.FEDORA: """
{c[1]} :/------------:// {r[0]}
{c[1]} :------------------:// {r[1]}
{c[1]} :-----------{c[0]}/shhdhyo/{c[1]}-:// {r[2]}
{c[1]} /-----------{c[0]}omMMMNNNMMMd/{c[1]}-:/ {r[3]}
{c[1]} :-----------{c[0]}sMMMdo:/{c[1]} -:/ {r[4]}
{c[1]} :-----------{c[0]}:MMMd{c[1]}------- --:/ {r[5]}
{c[1]} /-----------{c[0]}:MMMy{c[1]}------- ---/ {r[6]}
{c[1]} :------ --{c[0]}/+MMMh/{c[1]}-- ---: {r[7]}
{c[1]} :--- {c[0]}oNMMMMMMMMMNho{c[1]} -----: {r[8]}
{c[1]} :-- {c[0]}+shhhMMMmhhy++{c[1]} ------: {r[9]}
{c[1]} :- -----{c[0]}:MMMy{c[1]}--------------/ {r[10]}
{c[1]} :- ------{c[0]}/MMMy{c[1]}-------------: {r[11]}
{c[1]} :- ----{c[0]}/hMMM+{c[1]}------------: {r[12]}
{c[1]} :--{c[0]}:dMMNdhhdNMMNo{c[1]}-----------: {r[13]}
{c[1]} :---{c[0]}:sdNMMMMNds:{c[1]}----------: {r[14]}
{c[1]} :------{c[0]}:://:{c[1]}-----------:// {r[15]}
{c[1]} :--------------------:// {r[16]}
{c[1]} {r[17]}\n""",
Distributions.GENTOO: """
{c[1]} -/oyddmdhs+:. {r[0]}
{c[1]} -o{c[0]}dNMMMMMMMMNNmhy+{c[1]}-` {r[1]}
{c[1]} -y{c[0]}NMMMMMMMMMMMNNNmmdhy{c[1]}+- {r[2]}
{c[1]} `o{c[0]}mMMMMMMMMMMMMNmdmmmmddhhy{c[1]}/` {r[3]}
{c[1]} om{c[0]}MMMMMMMMMMMN{c[1]}hhyyyo{c[0]}hmdddhhhd{c[1]}o` {r[4]}
{c[1]} .y{c[0]}dMMMMMMMMMMd{c[1]}hs++so/s{c[0]}mdddhhhhdm{c[1]}+` {r[5]}
{c[1]} oy{c[0]}hdmNMMMMMMMN{c[1]}dyooy{c[0]}dmddddhhhhyhN{c[1]}d. {r[6]}
{c[1]} :o{c[0]}yhhdNNMMMMMMMNNNmmdddhhhhhyym{c[1]}Mh {r[7]}
{c[1]} .:{c[0]}+sydNMMMMMNNNmmmdddhhhhhhmM{c[1]}my {r[8]}
{c[1]} /m{c[0]}MMMMMMNNNmmmdddhhhhhmMNh{c[1]}s: {r[9]}
{c[1]} `o{c[0]}NMMMMMMMNNNmmmddddhhdmMNhs{c[1]}+` {r[10]}
{c[1]} `s{c[0]}NMMMMMMMMNNNmmmdddddmNMmhs{c[1]}/. {r[11]}
{c[1]} /N{c[0]}MMMMMMMMNNNNmmmdddmNMNdso{c[1]}:` {r[12]}
{c[1]} +M{c[0]}MMMMMMNNNNNmmmmdmNMNdso{c[1]}/- {r[13]}
{c[1]} yM{c[0]}MNNNNNNNmmmmmNNMmhs+/{c[1]}-` {r[14]}
{c[1]} /h{c[0]}MMNNNNNNNNMNdhs++/{c[1]}-` {r[15]}
{c[1]} `/{c[0]}ohdmmddhys+++/:{c[1]}.` {r[16]}
{c[1]} `-//////:--. {r[17]}\n""",
Distributions.KALI_LINUX: """
{c[0]} ,..... \0
{c[0]} ----` `..,;:ccc,. \0
{c[0]} ......''';lxO. {r[0]}
{c[0]} .....''''..........,:ld; {r[1]}
{c[0]} .';;;:::;,,.x, {r[2]}
{c[0]} ..'''. 0Xxoc:,. ... {r[3]}
{c[0]} .... ,ONkc;,;cokOdc',. {r[4]}
{c[0]} . OMo ':{c[1]}d{c[0]}o. {r[5]}
{c[0]} dMc :OO; {r[6]}
{c[0]} 0M. .:o. {r[7]}
{c[0]} ;Wd {r[8]}
{c[0]} ;XO, {r[9]}
{c[0]} ,d0Odlc;,.. {r[10]}
{c[0]} ..',;:cdOOd::,. {r[11]}
{c[0]} .:d;.':;. {r[12]}
{c[0]} 'd, .' {r[13]}
{c[0]} ;l .. {r[14]}
{c[0]} .o {r[15]}
{c[0]} c {r[16]}
{c[0]} .' {r[17]}
{c[0]} . \n""",
Distributions.MANJARO_LINUX: """
{c[0]} $$$$$$$$$$$$$$$$ $$$$$$$ {r[0]}
{c[0]} M77777777777777M M77777M {r[1]}
{c[0]} M77777777777777M M77777M {r[2]}
{c[0]} M77777MMMMMMMMMM M77777M {r[3]}
{c[0]} M77777M M77777M {r[4]}
{c[0]} M77777M $$$$$$$ M77777M {r[5]}
{c[0]} MMMMMMM M77777M M77777M {r[6]}
{c[0]} M77777M M77777M {r[7]}
{c[0]} $$$$$$$ M77777M M77777M {r[8]}
{c[0]} M77777M M77777M M77777M {r[9]}
{c[0]} M77777M M77777M M77777M {r[10]}
{c[0]} M77777M M77777M M77777M {r[11]}
{c[0]} M77777M M77777M M77777M {r[12]}
{c[0]} M77777M M77777M M77777M {r[13]}
{c[0]} M77777M M77777M M77777M {r[14]}
{c[0]} M77777M M77777M M77777M {r[15]}
{c[0]} M77777M M77777M M77777M {r[16]}
{c[0]} MMMMMMM MMMMMMM MMMMMMM {r[17]}\n""",
Distributions.LINUX: """
{c[1]} {r[0]}
{c[1]} a8888b. {r[1]}
{c[1]} d888888b. {r[2]}
{c[1]} 8P"YP"Y88 {r[3]}
{c[1]} 8|o||o|88 {r[4]}
{c[1]} 8{c[0]}\\vvvv/{c[1]}88 {r[5]}
{c[1]} 8{c[0]} \\vv/ {c[1]}Y8. {r[6]}
{c[1]} d/ {c[0]}`'{c[1]} \8b. {r[7]}
{c[1]} .dP . Y8b. {r[8]}
{c[1]} d8:' " `::88b. {r[9]}
{c[1]} d8" `Y88b {r[10]}
{c[1]} :8P ' :888 {r[11]}
{c[1]} 8a. : _a88P {r[12]}
{c[1]} {c[0]}._/"{c[1]}Yaa_ : .{c[0]}| {c[1]}88P{c[0]}|{c[1]} {r[13]}
{c[1]} {c[0]}\++++{c[1]}YP" `{c[0]}| {c[1]}8P{c[0]}++\.{c[1]} {r[14]}
{c[1]} {c[0]}/+++++\.{c[1]}_____.d{c[0]}|+++++/{c[1]} {r[15]}
{c[1]} {c[0]}\++++++){c[1]}888888P{c[0]}\+++/{c[1]} {r[16]}
{c[1]} {r[17]}\n""",
Distributions.LINUX_MINT: """
{c[1]} {r[0]}
{c[1]} MMMMMMMMMMMMMMMMMMMMMMMMMmds+. {r[1]}
{c[1]} MMm----::-://////////////oymNMd+` {r[2]}
{c[1]} MMd {c[0]}/++{c[1]} -sNMd: {r[3]}
{c[1]} MMNso/` {c[0]}dMM{c[1]} {c[0]}`.::-. .-::.`{c[1]} .hMN: {r[4]}
{c[1]} ddddMMh {c[0]}dMM{c[1]} {c[0]}:hNMNMNhNMNMNh:`{c[1]} NMm {r[5]}
{c[1]} NMm {c[0]}dMM{c[1]} {c[0]}.NMN/-+MMM+-/NMN`{c[1]} dMM {r[6]}
{c[1]} NMm {c[0]}dMM{c[1]} {c[0]}-MMm{c[1]} {c[0]}`MMM{c[1]} {c[0]}dMM.{c[1]} dMM {r[7]}
{c[1]} NMm {c[0]}dMM{c[1]} {c[0]}-MMm{c[1]} {c[0]}`MMM{c[1]} {c[0]}dMM.{c[1]} dMM {r[8]}
{c[1]} NMm {c[0]}dMM{c[1]} {c[0]}.mmd{c[1]} {c[0]}`mmm{c[1]} {c[0]}yMM.{c[1]} dMM {r[9]}
{c[1]} NMm {c[0]}dMM`{c[1]} {c[0]}..`{c[1]} {c[0]}`...{c[1]} {c[0]}ydm.{c[1]} dMM {r[10]}
{c[1]} hMM- {c[0]}+MMd/-------...-:sdds{c[1]} MMM {r[11]}
{c[1]} -NMm- {c[0]}:hNMNNNmdddddddddy/`{c[1]} dMM {r[12]}
{c[1]} -dMNs-``{c[0]}-::::-------.``{c[1]} dMM {r[13]}
{c[1]} `/dMNmy+/:-------------:/yMMM {r[14]}
{c[1]} ./ydNMMMMMMMMMMMMMMMMMMMMM {r[15]}
{c[1]} {r[16]}
{c[1]} {r[17]}\n""",
Distributions.OPENSUSE: """
{c[0]} .;ldkO0000Okdl;. {r[0]}
{c[0]} .;d00xl:^''''''^:ok00d;. {r[1]}
{c[0]} .d00l' 'o00d. {r[2]}
{c[0]} .d0Kd' {c[1]}Okxol:;,.{c[0]} :O0d. {r[3]}
{c[0]} .OK{c[1]}KKK0kOKKKKKKKKKKOxo:,{c[0]} lKO. {r[4]}
{c[0]} ,0K{c[1]}KKKKKKKKKKKKKKK0P^{c[0]},,,{c[1]}^dx:{c[0]} ;00, {r[5]}
{c[0]} .OK{c[1]}KKKKKKKKKKKKKKKk'{c[0]}.oOPPb.{c[1]}'0k.{c[0]} cKO. {r[6]}
{c[0]} :KK{c[1]}KKKKKKKKKKKKKKK: {c[0]}kKx..dd{c[1]} lKd{c[0]} 'OK: {r[7]}
{c[0]} dKK{c[1]}KKKKKKKKKOx0KKKd {c[0]}^0KKKO'{c[1]} kKKc{c[0]} dKd {r[8]}
{c[0]} dKK{c[1]}KKKKKKKKKK;.;oOKx,..{c[0]}^{c[1]}..;kKKK0.{c[0]} dKd {r[9]}
{c[0]} :KK{c[1]}KKKKKKKKKK0o;...^cdxxOK0O/^^'{c[0]} .0K: {r[10]}
{c[0]} kKK{c[1]}KKKKKKKKKKKKK0x;,,......,;od{c[0]} lKk {r[11]}
{c[0]} '0K{c[1]}KKKKKKKKKKKKKKKKKKKK00KKOo^{c[0]} c00' {r[12]}
{c[0]} 'kK{c[1]}KKOxddxkOO00000Okxoc;''{c[0]} .dKk' {r[13]}
{c[0]} l0Ko. .c00l' {r[14]}
{c[0]} 'l0Kk:. .;xK0l' {r[15]}
{c[0]} 'lkK0xl:;,,,,;:ldO0kl' {r[16]}
{c[0]} '^:ldxkkkkxdl:^' {r[17]}\n""",
Distributions.RED_HAT: """
{c[1]} {r[0]}
{c[1]} {c[2]}\`.-..........\`{c[1]} {r[1]}
{c[1]} {c[2]}\`////////::.\`-/.{c[1]} {r[2]}
{c[1]} {c[2]}-: ....-////////.{c[1]} {r[3]}
{c[1]} {c[2]}//:-::///////////\`{c[1]} {r[4]}
{c[1]} {c[2]}\`--::: \`-://////////////:{c[1]} {r[5]}
{c[1]} {c[2]}//////- \`\`.-://///////{c[1]} .\` {r[6]}
{c[1]} {c[2]}\`://////:-.\` :///////::///:\`{c[1]} {r[7]}
{c[1]} {c[2]}.-/////////:---/////////////:{c[1]} {r[8]}
{c[1]} {c[2]}.-://////////////////////.{c[1]} {r[9]}
{c[1]} {c[0]}yMN+\`.-${c[2]}::///////////////-\`{c[1]} {r[10]}
{c[1]} {c[0]}.-\`:NMMNMs\` \`..-------..\`{c[1]} {r[11]}
{c[1]} {c[0]}MN+/mMMMMMhoooyysshsss{c[1]} {r[12]}
{c[1]} {c[0]}MMM MMMMMMMMMMMMMMyyddMMM+{c[1]} {r[13]}
{c[1]} {c[0]}MMMM MMMMMMMMMMMMMNdyNMMh\` hyhMMM{c[1]} {r[14]}
{c[1]} {c[0]}MMMMMMMMMMMMMMMMyoNNNMMM+. MMMMMMMM{c[1]} {r[15]}
{c[1]} {c[0]}MMNMMMNNMMMMMNM+ mhsMNyyyyMNMMMMsMM{c[1]} {r[16]}
{c[1]} {r[17]}\n""",
Distributions.UBUNTU: """
{c[1]} {c[0]}.oyhhs:{c[1]} {r[0]}
{c[1]} ..--.., {c[0]}shhhhhh-{c[1]} {r[1]}
{c[1]} -+++++++++`:{c[0]}yyhhyo`{c[1]} {r[2]}
{c[1]} {c[2]}.--{c[1]} -++++++++/-.-{c[0]}::-`{c[1]} {r[3]}
{c[1]} {c[2]}.::::-{c[1]} :-----:/+++/++/. {r[4]}
{c[1]} {c[2]}-:::::-.{c[1]} .:++++++: {r[5]}
{c[1]} ,,, {c[2]}.:::::-`{c[1]} .++++++- {r[6]}
{c[1]} ./+++/-{c[2]}`-::-{c[1]} ./////: {r[7]}
{c[1]} +++++++ {c[2]}.::-{c[1]} {r[8]}
{c[1]} ./+++/-`{c[2]}-::-{c[1]} {c[0]}:yyyyyo{c[1]} {r[9]}
{c[1]} ``` `{c[2]}-::::-`{c[1]} {c[0]}:yhhhhh:{c[1]} {r[10]}
{c[1]} {c[2]}-:::::-.{c[1]} {c[0]}`-ohhhhhh+{c[1]} {r[11]}
{c[1]} {c[2]}.::::-`{c[1]} {c[0]}-o+///+oyhhyyyhy:{c[1]} {r[12]}
{c[1]} {c[2]}`.--{c[1]} {c[0]}/yhhhhhhhy+{c[2]},....{c[1]} {r[13]}
{c[1]} {c[0]}/hhhhhhhhh{c[2]}-.-:::;{c[1]} {r[14]}
{c[1]} {c[0]}`.:://::- {c[2]}-:::::;{c[1]} {r[15]}
{c[1]} {c[2]}`.-:-'{c[1]} {r[16]}
{c[1]} {r[17]}\n""",
Distributions.WINDOWS: """
{c[1]} {r[0]}
{c[1]} {c[0]},.=:^!^!t3Z3z.,{c[1]} {r[1]}
{c[1]} {c[0]}:tt:::tt333EE3{c[1]} {r[2]}
{c[1]} {c[0]}Et:::ztt33EEE{c[1]} {c[2]}@Ee.,{c[1]} {c[2]}..,{c[1]} {r[3]}
{c[1]} {c[0]};tt:::tt333EE7{c[1]} {c[2]};EEEEEEttttt33#{c[1]} {r[4]}
{c[1]} {c[0]}:Et:::zt333EEQ.{c[1]} {c[2]}SEEEEEttttt33QL{c[1]} {r[5]}
{c[1]} {c[0]}it::::tt333EEF{c[1]} {c[2]}@EEEEEEttttt33F{c[1]} {r[6]}
{c[1]} {c[0]};3=*^```'*4EEV{c[1]} {c[2]}:EEEEEEttttt33@.{c[1]} {r[7]}
{c[1]} ,.=::::it=.,{c[0]} `{c[1]} {c[2]}@EEEEEEtttz33QF{c[1]} {r[8]}
{c[1]} ;::::::::zt33){c[1]} {c[2]}'4EEEtttji3P*{c[1]} {r[9]}
{c[1]} :t::::::::tt33.{c[3]}:Z3z..{c[2]} `` {c[3]},..g.{c[1]} {r[10]}
{c[1]} i::::::::zt33F{c[1]} {c[3]}AEEEtttt::::ztF{c[1]} {r[11]}
{c[1]} ;:::::::::t33V{c[1]} {c[3]};EEEttttt::::t3{c[1]} {r[12]}
{c[1]} E::::::::zt33L{c[1]} {c[3]}@EEEtttt::::z3F{c[1]} {r[13]}
{c[1]} {{3=*^```'*4E3){c[1]} {c[3]};EEEtttt:::::tZ`{c[1]} {r[14]}
{c[1]} `{c[1]} {c[3]}:EEEEtttt::::z7{c[1]} {r[15]}
{c[1]} {c[3]}'VEzjt:;;z>*`{c[1]} {r[16]}
{c[1]} {r[17]}\n"""
}
# ----------- Configuration ----------- #
class Configuration():
def __init__(self):
"""
Represents the default configuration which will be used by Archey.
Values present in the dictionary below are needed.
New optional values may be added with `updateRecursive()` method.
"""
self.config = {
'colors_palette': {
'use_unicode': False
},
'default_strings': {
'no_address': 'No Address',
'not_detected': 'Not detected'
},
'ip_settings': {
'lan_ip_max_count': 2,
'wan_ip_v6_support': True
},
'temperature': {
'char_before_unit': ' ',
'use_fahrenheit': False
},
'timeout': {
'ipv4_detection': 1,
'ipv6_detection': 1
}
}
def get(self, key, default=None):
"""
A binding method to imitate the `dict.get()` behavior.
"""
return self.config.get(key, default)
def loadConfiguration(self, path):
# If a previous configuration file has denied overriding...
if not self.config.get('allow_overriding', True):
# ... don't load this one.
return
if not path.endswith('/'):
path += '/'
path += 'config.json'
try:
with open(path) as file:
self.updateRecursive(self.config, json.load(file))
except FileNotFoundError:
pass
# For backward compatibility with Python versions prior to 3.5.0
# we use `ValueError` instead of `json.JSONDecodeError`.
except ValueError as e:
print('Warning: {0} ({1})'.format(e, path), file=sys.stderr)
def updateRecursive(self, oldDict, newDict):
"""
A method for recursively merging dictionaries as...
... `dict.update()` is not able to do this.
Original snippet taken from here :
https://gist.github.com/angstwad/bf22d1822c38a92ec0a9
"""
for key, value in newDict.items():
if key in oldDict and isinstance(oldDict[key], dict) \
and isinstance(value, dict):
self.updateRecursive(oldDict[key], value)
else:
oldDict[key] = value
# ---------- Global variables --------- #
# We create a global instance of our `Configuration` Class
config = Configuration()
# We'll list the running processes only one time
try:
PROCESSES = check_output([
'ps',
'-u' + str(getuid()) if getuid() != 0 else '-ax',
'-o', 'comm', '--no-headers'
]).decode().split('\n')
except FileNotFoundError:
print('Please, install first `procps` on your distribution.',
file=sys.stderr)
exit()
# -------------- Classes -------------- #
class Output:
def __init__(self):
self.results = []
try:
lsbOutput = check_output([
'lsb_release', '-i', '-s'
]).decode().rstrip()
except FileNotFoundError:
print('Please, install first `lsb-release` on your distribution.',
file=sys.stderr)
exit()
if re.search('Microsoft',
check_output(['uname', '-r']).decode().rstrip()):
self.distribution = Distributions.WINDOWS
else:
for distribution in Distributions:
if re.fullmatch(distribution.value, lsbOutput):
self.distribution = distribution
break
else:
self.distribution = Distributions.LINUX
def append(self, key, value):
self.results.append('{0}{1}:{2} {3}'.format(
colorDict[self.distribution][1], key, colorDict['clear'], value))
def output(self):
results = []
results.extend([''] * floor((18 - len(self.results)) / 2))
results.extend(self.results[:])
if len(results) < 18:
results.extend([''] * (18 - len(results)))
print(logosDict[self.distribution].format(
c=colorDict[self.distribution], r=results) + colorDict['clear'])
class User:
def __init__(self):
self.value = getenv('USER',
config.get('default_strings')['not_detected'])
class Hostname:
def __init__(self):
self.value = check_output(['uname', '-n']).decode().rstrip()
class Model:
def __init__(self):
try:
with open('/sys/devices/virtual/dmi/id/product_name') as file:
model = file.read().rstrip()
except FileNotFoundError:
# The file above does not exist, is this device a Raspberry Pi ?
# Let's retrieve the Hardware and Revision IDs with `/proc/cpuinfo`
with open('/proc/cpuinfo') as file:
output = file.read()
hardware = re.search('(?<=Hardware\t: ).*', output)
revision = re.search('(?<=Revision\t: ).*', output)
# If the output contains 'Hardware' and 'Revision'...
if hardware and revision:
# ... let's set a pretty info string with these data
model = 'Raspberry Pi ' + hardware.group(0) + \
' (Rev. ' + revision.group(0) + ')'
else:
# A tricky way to retrieve some details about hypervisor...
# ... within virtualized contexts.
# `archey` needs to be run as root although.
try:
virtWhat = ', '.join(check_output([
'virt-what'
],
stderr=DEVNULL)
.decode().split())
if virtWhat:
try:
# Sometimes we may gather info added by...
# ... hosting service provider this way
model = check_output([
'dmidecode',
'-s',
'system-product-name'
], stderr=DEVNULL
).decode().rstrip()
except (FileNotFoundError, CalledProcessError):
model = 'Virtualized environment'
model += ' (' + virtWhat + ')'
else:
model = 'Bare-metal environment'
except (FileNotFoundError, CalledProcessError):
model = config.get('default_strings')['not_detected']
self.value = model
class Distro:
def __init__(self):
self.value = check_output(['lsb_release', '-d', '-s']
).decode().rstrip() + \
' ' + check_output(['uname', '-m']).decode().rstrip()
class Kernel:
def __init__(self):
self.value = check_output(['uname', '-r']).decode().rstrip()
class Uptime:
def __init__(self):
with open('/proc/uptime') as file:
fuptime = int(file.read().split('.')[0])
day, fuptime = divmod(fuptime, 86400)
hour, fuptime = divmod(fuptime, 3600)
minute = fuptime // 60
uptime = ''
uptime += ((str(day) + ' day' + ('s' if day > 1 else '') + ('' if minute == 0 and hour == 0 else (' and ' if (minute != 0 and hour == 0) or (minute == 0 and hour != 0) else ', '))) if day >= 1 else '')
uptime += ((str(hour) + ' hour' + ('s' if hour > 1 else '') + (' and ' if minute != 0 else '')) if hour >= 1 else '')
uptime += ((str(minute) + ' minute' + ('s' if minute > 1 else '')) if minute >= 1 else ('< 1 minute' if day == 0 and hour == 0 else ''))
self.value = uptime
class WindowManager:
def __init__(self):
try:
wm = re.search('(?<=Name: ).*',
check_output(['wmctrl', '-m'],
stderr=DEVNULL).decode()).group(0)
except (FileNotFoundError, CalledProcessError):
for key in wmDict.keys():
if key in PROCESSES:
wm = wmDict[key]
break
else:
wm = config.get('default_strings')['not_detected']
self.value = wm
class DesktopEnvironment:
def __init__(self):
for key in deDict.keys():
if key in PROCESSES:
de = deDict[key]
break
else:
# Let's rely on an environment var if the loop above didn't `break`
de = getenv('XDG_CURRENT_DESKTOP',
config.get('default_strings')['not_detected'])
self.value = de
class Shell:
def __init__(self):
self.value = getenv('SHELL',
config.get('default_strings')['not_detected'])
class Terminal:
def __init__(self):
terminal = getenv('TERM',
config.get('default_strings')['not_detected'])
# On systems with non-Unicode locales, we imitate '\u2588' character
# ... with '#' to display the terminal colors palette.
# This is the default option for backward compatibility.
colors = ' '.join([
'\x1b[0;3{0}m{1}\x1b[1;3{0}m{1}{2}'.format(
i,
'\u2588' if config.get('colors_palette')['use_unicode']
else '#',
colorDict['clear']
) for i in range(7, 0, -1)])
self.value = terminal + ' ' + colors
class Temperature:
def __init__(self):
temps = []
try:
# Let's try to retrieve a value from 'Broadcom' chip on Raspberry
temp = float(re.findall(
'\d+\.\d+',
check_output(['/opt/vc/bin/vcgencmd', 'measure_temp'],
stderr=DEVNULL).decode())[0])
temps.append(
self.convertToFahrenheit(temp)
if config.get('temperature')['use_fahrenheit'] else temp)
except (FileNotFoundError, CalledProcessError):
pass
# Now we just check for values within files present in the path below
for thermalFile in glob('/sys/class/thermal/thermal_zone*/temp'):
with open(thermalFile) as file:
temp = float(file.read().strip()) / 1000
if temp != 0.0:
temps.append(
self.convertToFahrenheit(temp)
if config.get('temperature')['use_fahrenheit']
else temp)
if temps:
self.value = '{0}{3}{2} (Max. {1}{3}{2})'.format(
str(round(sum(temps) / len(temps), 1)),
str(round(max(temps), 1)),
'F' if config.get('temperature')['use_fahrenheit'] else 'C',
config.get('temperature')['char_before_unit'])
else:
self.value = config.get('default_strings')['not_detected']
"""
Simple Celsius to Fahrenheit conversion method
"""
def convertToFahrenheit(self, temp):
return temp * (9 / 5) + 32
class Packages:
def __init__(self):
for packagesTool in [['pacman', '-Q'],
['dnf', 'list', 'installed'],
['dpkg', '--get-selections'],
['zypper', 'search', '--installed-only'],
['emerge', '-ep', 'world'],
['rpm', '-qa']]:
try:
results = check_output(packagesTool, stderr=DEVNULL).decode()
packages = results.count('\n')
if 'dpkg' in packagesTool:
packages -= results.count('deinstall')
break
except (FileNotFoundError, CalledProcessError):
pass
else:
packages = config.get('default_strings')['not_detected']
self.value = packages
class CPU:
def __init__(self):
with open('/proc/cpuinfo') as file:
self.value = re.sub('\s+', ' ',
re.search('(?<=model name\t: ).*',
file.read()).group(0))
class GPU:
def __init__(self):
try:
gpuinfo = check_output(['grep', '-E', '3D|VGA|Display'],
stdin=Popen(['lspci'],
stdout=PIPE,
stderr=DEVNULL).stdout
).decode().split(': ')[1].rstrip()
# If the line got too long, let's truncate it and add some dots...
if len(gpuinfo) > 48:
gpuinfo = re.findall('.{1,45}(?:\W|$)',
gpuinfo)[0].strip() + '...'
except (FileNotFoundError, CalledProcessError):
gpuinfo = config.get('default_strings')['not_detected']
self.value = gpuinfo
class RAM:
def __init__(self):
try:
ram = ''.join(filter(re.compile('M').search,
Popen(['free', '-m'], stdout=PIPE, env={'LANG': 'C'}
).communicate()[0].decode().split('\n'))
).split()
used = float(ram[2])
total = float(ram[1])
except (IndexError, FileNotFoundError):
# An in-digest one-liner to retrieve memory info into a dictionary
with open('/proc/meminfo') as file:
ram = {
i.split(':')[0]: float(i.split(':')[1].strip(' kB')) / 1024
for i in filter(None, file.read().split('\n'))
}
total = ram['MemTotal']
# Here, let's imitate the `free` command behavior
# (https://gitlab.com/procps-ng/procps/blob/master/proc/sysinfo.c#L787)
used = total - (ram['MemFree'] + ram['Cached'] + ram['Buffers'])
if used < 0:
used += ram['Cached'] + ram['Buffers']
self.value = '{0}{1} MB{2} / {3} MB'.format(
colorDict['sensors'][
int(((used / total) * 100) // 33.34)
],
int(used), colorDict['clear'], int(total))
class Disk:
def __init__(self):
total = re.sub(',', '.',
check_output([
'df', '-Tlh', '-B', 'GB', '--total',
'-t', 'ext4', '-t', 'ext3', '-t', 'ext2',
'-t', 'reiserfs', '-t', 'jfs', '-t', 'zfs',
'-t', 'ntfs', '-t', 'fat32', '-t', 'btrfs',
'-t', 'fuseblk', '-t', 'xfs',
'-t', 'simfs', '-t', 'tmpfs'
]).decode().splitlines()[-1]).split()
self.value = '{0}{1}{2} / {3}'.format(
colorDict['sensors'][
int(float(total[5][:-1]) // 33.34)
],
re.sub('GB', ' GB', total[3]), colorDict['clear'],
re.sub('GB', ' GB', total[2]))
class LAN_IP:
def __init__(self):
try:
addresses = check_output(['hostname', '-I'], stderr=DEVNULL
).decode().split()
except (CalledProcessError, FileNotFoundError):
# Slow manual workaround for old `inetutils` versions, with `ip`
addresses = check_output(
['cut', '-d', ' ', '-f', '4'],
stdin=Popen(['cut', '-d', '/', '-f', '1'],
stdout=PIPE,
stdin=Popen(['tr', '-s', ' '],
stdout=PIPE,
stdin=Popen(['grep', '-E',
'scope (global|site)'
], stdout=PIPE,
stdin=Popen(['ip',
'-o',
'addr',
'show',
'up'],
stdout=PIPE
).stdout
).stdout
).stdout
).stdout
).decode().split()
# Use list slice to save only `lan_ip_max_count` addresses.
# If set to `False`, don't modify the list.
# This option is still optional.
self.value = ', '.join(addresses[:(
config.get('ip_settings')['lan_ip_max_count']
if config.get('ip_settings')['lan_ip_max_count'] is not False
else len(addresses)
)]) or config.get('default_strings')['no_address']
class WAN_IP:
def __init__(self):
# IPv6 address retrieval (unless the user doesn't want it).
if config.get('ip_settings')['wan_ip_v6_support']:
try:
ipv6_value = check_output([
'dig', '+short', '-6', 'aaaa', 'myip.opendns.com',
'@resolver1.ipv6-sandbox.opendns.com'
], timeout=config.get('timeout')['ipv6_detection'],
stderr=DEVNULL).decode().rstrip()
except (FileNotFoundError, TimeoutExpired, CalledProcessError):
try:
ipv6_value = check_output([
'wget', '-qO-', 'https://v6.ident.me/'
], timeout=config.get('timeout')['ipv6_detection']
).decode()
except (CalledProcessError, TimeoutExpired):
# It looks like this user doesn't have any IPv6 address...
# ... or is not connected to Internet.
ipv6_value = None
except FileNotFoundError:
ipv6_value = None
print('Warning: `wget` has not been found on your system.',
file=sys.stderr)
else:
ipv6_value = None
# IPv4 addresses retrieval (anyway).
try:
ipv4_value = check_output([
'dig', '+short', 'myip.opendns.com', '@resolver1.opendns.com'
], timeout=config.get('timeout')['ipv4_detection'],
stderr=DEVNULL).decode().rstrip()
except (FileNotFoundError, TimeoutExpired, CalledProcessError):
try:
ipv4_value = check_output([
'wget', '-qO-', 'https://v4.ident.me/'
], timeout=config.get('timeout')['ipv4_detection']
).decode()
except (CalledProcessError, TimeoutExpired):
# This user looks not connected to Internet...
ipv4_value = None
except FileNotFoundError:
ipv4_value = None
# If statement so as to not print the same message twice.
if not config.get('ip_settings')['wan_ip_v6_support']:
print('Warning: `wget` has not been found on your system.',
file=sys.stderr)
self.value = ', '.join(
'{0}\n{1}'.format(ipv4_value or '', ipv6_value or '').split()
) or config.get('default_strings')['no_address']
# -------------- Classes' Enumeration -------------- #
class Classes(Enum):
User = User
Hostname = Hostname
Model = Model
Distro = Distro
Kernel = Kernel
Uptime = Uptime
WindowManager = WindowManager
DesktopEnvironment = DesktopEnvironment
Shell = Shell
Terminal = Terminal
Packages = Packages
Temperature = Temperature
CPU = CPU
GPU = GPU
RAM = RAM
Disk = Disk
LAN_IP = LAN_IP
WAN_IP = WAN_IP
# -------------- Main -------------- #
if __name__ == '__main__':
# Load global and local configurations in a "regular" order (all optional)
config.loadConfiguration('/etc/archey4/')
config.loadConfiguration(os.path.expanduser('~') + '/.config/archey4/')
config.loadConfiguration(os.path.dirname(os.path.realpath(__file__)))
output = Output()
for key in Classes:
if config.get('entries', {}).get(key.name, True):
output.append(key.name, key.value().value)
output.output()