You are not logged in.

#951 2010-03-29 21:13:05

Ogion
Member
From: Germany
Registered: 2007-12-11
Posts: 367

Re: Post your handy self made command line utilities

#/bin/zsh
# This scripts generates a list of your installed packages,
# sorted by the number of each packages' number of deps,
# most deps at the top
# (It checks the depends file in the folder of each package
# in the local package database)

for i in $(/bin/ls -1 -d /var/lib/pacman/local/*)
do
 numberdeps=$(perl -00 -ne 'print if /%DEPENDS/' $i/depends | sed -e '/^$/d' -e '/DEPENDS/d' | wc -l)
 echo "$numberdeps $(basename $i)" >> tmpfile
done
sort -g --reverse tmpfile > pkgsbydepnumber
rm tmpfile
echo "File pkgsbynumber created."

What it says in the comments. Was just a little curiosity about the number of dependencies packages have.
(probably can use bash or others as well. Also there are probably ways to use grep/awk/sed or so for the perl part, it gets the part of %DEPENDS% and not the %OPTDEPENDS% in the depends files).

Ogion


(my-dotfiles)
"People willing to trade their freedom for temporary security deserve neither and will lose both." - Benjamin Franklin
"Enlightenment is man's leaving his self-caused immaturity." - Immanuel Kant

Offline

#952 2010-03-30 15:46:20

Daenyth
Forum Fellow
From: Boston, MA
Registered: 2008-02-24
Posts: 1,244

Re: Post your handy self made command line utilities

I just wrote this git pre-commit hook. It's intended for those who use git to manage their PKGBUILD files. It forbids all commits that include a PKGBUILD file when the source and install files are not either in git, or also in the commit.

Install it by putting it into .git/hooks/pre-commit and making it mode +x.

Source here

Offline

#953 2010-03-30 20:09:13

Yannick_LM
Member
Registered: 2008-12-22
Posts: 142

Re: Post your handy self made command line utilities

> This pre-commit hook may be disabled with `git config hooks.missingsources.allow true`

This is very clever.
I'll think about this when writing git hooks.

Offline

#954 2010-03-30 20:15:21

Daenyth
Forum Fellow
From: Boston, MA
Registered: 2008-02-24
Posts: 1,244

Re: Post your handy self made command line utilities

I googled for some example pre-commit hooks to learn from and saw a similar construction used smile

In the future, I plan to add md5sum checking to make sure that the if the PKGBUILD has changed md5sums for the sources, that the sources also get committed.

Last edited by Daenyth (2010-03-30 20:16:17)

Offline

#955 2010-03-30 20:58:44

Pajaro
Member
Registered: 2004-04-21
Posts: 884

Re: Post your handy self made command line utilities

like say.sh in the beggining of the topic, but in one line, and it works with unicode input, needed for nonenglish languages
english

/usr/bin/di

#!/bin/bash
echo "$@" | iconv --from-code=UTF-8 --to-code=ISO-8859-15 | festival --tts --language spanish

Offline

#956 2010-04-01 17:29:12

nightchill
Member
Registered: 2009-03-25
Posts: 16

Re: Post your handy self made command line utilities

Been doing a front end to ngrep because friend pissed me off with constant "what to put where" questions so if someone wants it:

#!/bin/bash

#nwrap ngrep wrapper for dummies
#version (one and only) 1
#license i dont care
#deps (obviously) ngrep & dialog

#by night <no@mail.xxx>

#intro
clear

#depcheck
if command -v dialog >/dev/null && command -v ngrep; then
    
    #set up us the bomb
    ifconfig > .ndevs
    DEV=".ndevs"

    #list network devices
    dialog --title "Network devices" --exit-label "NEXT" --textbox $DEV 22 70 2>&1 >/dev/tty || exit
    rm $DEV

    #grab input
    dev=$(dialog --inputbox "Your network device (eth0, wlan0, etc)?" 8 40 2>&1 >/dev/tty) || exit
    pattern=$(dialog --inputbox "Pattern (split with '|')?" 8 40 2>&1 >/dev/tty) || exit
    ip=$(dialog --inputbox "Target host (leave blank for general traffic)?" 8 40 2>&1 >/dev/tty) || exit

    #go ninja until sigterm
    clear; printf "ngrep needs to be run as root so please enter your password below\n\n"
    if [ -n "$ip" ]; then
        su -c "ngrep -d $dev -P '' -W byline -iq '$pattern' host $ip"
    else
        su -c "ngrep -d $dev -P '' -W byline -iq '$pattern'"
    fi

#depcheck fail
else
    printf "You are missing one of these deps: dialog and/or ngrep.\n"; sleep 1; printf "Bye!"; sleep 1; clear
fi

exit 0

Offline

#957 2010-04-02 03:38:43

Gen2ly
Member
From: Sevierville, TN
Registered: 2009-03-06
Posts: 1,529
Website

Re: Post your handy self made command line utilities

nightchill, hmm looks interesting.  I think I may have done something similiar with grep but the script had some flaws and I don't use it much any more.  Is this script to make searching through files.  For my script with grep I created a syntax 'grop <pattern> <location>'.  Is this what you're trying to do here?  Also, why the use of ngrep besides regular grep?


Setting Up a Scripting Environment | Proud donor to wikipedia - link

Offline

#958 2010-04-02 11:31:21

nightchill
Member
Registered: 2009-03-25
Posts: 16

Re: Post your handy self made command line utilities

Gen2ly wrote:

nightchill, hmm looks interesting.  I think I may have done something similiar with grep but the script had some flaws and I don't use it much any more.  Is this script to make searching through files.  For my script with grep I created a syntax 'grop <pattern> <location>'.  Is this what you're trying to do here?  Also, why the use of ngrep besides regular grep?

ngrep is grep features for network so you can match your patterns against payload of packets. So this script doesn't search thru files, basically doesn't do anything you couldn't make ngrep to do if you do man ngrep. But like I said, my friend couldn't do even that so I wrote this basic interface to ngrep with some formatting options included.

Offline

#959 2010-04-02 12:18:30

Berticus
Member
Registered: 2008-06-11
Posts: 731

Re: Post your handy self made command line utilities

I've got this as a function in my .zshrc file, here it is as a standalone script:

#!/bin/zsh
if [ $# -eq 2 ]
then
    if [ $(2#*.} = "pdf" ]
    then
        pdftotext -q -nopgbrk "$2" - | grep --color=always -niEe "$1"
        return 0
    else
        searchpath=($(ls $2/**/*.pdf))
    fi
elif [ $# -eq 1 ]
then
    searchpath=($(ls **/*.pdf))
else
    echo "Usage: pdfgrep PATTERN [FILE|DIRECTORY]"
fi

for i in $searchpath
do
    results=$(pdftotext -q -nopgbrk "$i" - | grep --color=always -niEe "$1")
    if [ $results ]
    then
        echo -e "\nFile: $i"
        echo -e "$results"
    fi
done

I call it pdfgrep. Not really sure if I'll dedicate more time into making it more efficient (how it handles parameters). Zsh's extended globbing needs to be enabled.

It's a function/script that greps pdf files. If you provide a file, it'll grep that particular file. If you provide a directory or only a pattern, it recursively greps the directory. Might throw in an option to turn off the recursive search.

Last edited by Berticus (2010-04-09 17:39:59)

Offline

#960 2010-04-04 06:36:49

kittykatt
Member
From: Missouri, USA
Registered: 2009-11-04
Posts: 260
Website

Re: Post your handy self made command line utilities

Just wanted to post here about my script, screenFetch, which is written totally from scratch in Bash. big_smile

Links for it:
http://aur.archlinux.org/packages.php?ID=36072
http://github.com/KittyKatt/screenFetch

It's my first big project, so I'm kind of excited to share it. ^^


Here's some for stuff I've got sitting around!

My Random Wallpaper Snippet (wallchange)

#!/bin/bash
# Change the line below to the
# directory that has your wallpaper
# images
WALLPAPERS="$HOME/.wallpapers"
ALIST=( `ls -w1 $WALLPAPERS` )
RANGE=${#ALIST[*]}
SHOW=$(( $RANDOM % $RANGE ))

feh --bg-scale $WALLPAPERS/${ALIST[$SHOW]}

A small MPD status/control for the PekWM root menu that I hacked together

#!/usr/bin/env python
#
# Author: Brett Bohnenkamper <kittykatt@archlinux.us>
# License: GPL 2.0
#
# This script depends on py-libmpdclient2 which you can get from 
# http://incise.org/index.cgi/py-libmpdclient2
#
# Usage:
# Save this script as "mpd.py" in the folder ~/.pekwm/scripts/ and make it
# executable ("chmod +x ~/.pekwm/scripts/mpd.py" in terminal).
#
# Put an entry in ~/.pekwm/menu wherever you would like it to be displayed:
# Submenu = "MPD" {
#      Entry { Actions = "Dynamic /home/YOURUSERNAME/.pekwm/scripts/mpd.py" }
# }
#
# Please also make sure to replace the YOURUSERNAME above with your actual username
# in /home and replace all YOURUSERNAME values in the following code with your actual
# username in /home.
#
# Yes, I do know that some of this is awful coding. I'll fix it up as soon as I have time.
#
# Originally Based on the ob-mpd.py code for OpenBox by John Eikenberry <jae@zhar.net>
#
# Ported to PekWM by me. Enjoy.
#

import os, sys, socket
import mpdclient2

argv = sys.argv

# The default port for MPD is 6600.  If for some reason you have MPD
# running on a different port, change this setting.
mpdPort = 6600

# determin path to this file
# my_path = sys.modules[__name__].__file__
# if this fails for some reason, just set it manually.
# Eg.
my_path = "/home/YOURUSERNAME/.pekwm/scripts/mpd.py"


try:
    connect = mpdclient2.connect(port=mpdPort)
except socket.error:
    # If MPD is not running.
    if len(argv) > 1 and argv[1] == 'start':
            os.execl('/usr/bin/mpd','$HOME/.mpdconf')
    else:
        print "Dynamic {"
    print "SubMenu = \"MPD\" {"
        print "Entry = \"%s\" { Actions = \"Exec my_path \"%s\" &\" }" % ('MPD is not running  [start]','start')
    print "}"
        print "}"

else: # part of connect try block
        
    song = connect.currentsong()
    stats = connect.stats()
    status = connect.status()

    if status['state'] == "stop":
        display_state = "Not playing"
    else:
        try:
            display_state = "%s - %s" % (song.artist, song.title)
        except (AttributeError, KeyError): # no id3 tags
            display_state = os.path.basename(song.file)
        if status['state'] == "pause":
            display_state += " (paused)"
    display_state = display_state.replace('"',"'")
    display_state = display_state.replace('&','&')

    if len(argv) > 1:

        state = status.state
        def play():
            if state == "stop" or state == "pause":
                connect.play()

        def pause():
            if state == "play":
                connect.pause(1)
            elif state == "pause":
                connect.play()

        def stop():
            if state == "play" or state == "pause":
                connect.stop()

        def prev():
            if state == "play":
                connect.previous()

        def next():
            if state == "play":
                connect.next()

        random_state = int(status.random)
        def random():
            if random_state:
                connect.random(0)
            else:
                connect.random(1)

        repeat_state = int(status.repeat)
        def repeat():
            if repeat_state:
                connect.repeat(0)
            else:
                connect.repeat(1)

        single_state = int(status.single)
        def single():
            if single_state:
                connect.single(0)
            else:
                connect.single(1)

        consume_state = int(status.consume)
        def consume():
            if consume_state:
                connect.consume(0)
            else:
                connect.consume(1)

        def kill():
            try:
                connect.kill()
            except EOFError:
                pass

        def update():
            connect.update()
        
        def volume(setto):
            relative = (setto[0] in ['+','-'])
            setto = int(setto)
            if relative:
                newvol = int(status.volume) + setto
                newvol = newvol <= 100 or 100
                newvol = newvol >= 0 or 0
            connect.setvol(setto)

        def client():
            os.execlp('x-terminal-emulator','-e','ncmpc')

        def enable(output_id):
            connect.enableoutput(int(output_id))

        def disable(output_id):
            connect.disableoutput(int(output_id))

        def load(list_name):
            connect.load(list_name)

        def clear():
            connect.clear()

        if   (argv[1]     == "play"):    play()
        elif (argv[1]     == "pause"):   pause()
        elif (argv[1]     == "stop"):    stop()
        elif (argv[1][:4] == "prev"):    prev()
        elif (argv[1]     == "next"):    next()
        elif (argv[1]     == "random"):  random()
        elif (argv[1]     == "repeat"):  repeat()
        elif (argv[1]     == "volume"):  volume(argv[2])
        elif (argv[1]     == "client"):  client()
        elif (argv[1]     == "kill"):    kill()
        elif (argv[1]     == "update"):  update()
        elif (argv[1]     == "enable"):  enable(argv[2])
        elif (argv[1]     == "disable"): disable(argv[2])
        elif (argv[1]     == "load"):    load(argv[2])
        elif (argv[1]     == "clear"):   clear()

    else:
    print "Dynamic {"
    print "Submenu = \"Stats\" {"
        print "Entry = \"%s\" { Actions = \"mpc current\" }" % (display_state)
        print "Entry = \"%s\" { Actions = \"Exec echo 'kbs'\" }" % ('%s kbs' % status.bitrate)
    print "Separator {}"
        print "Entry = \"%s\" { Actions = \"Exec echo 'artists'\" }" % ("Artists in DB: %s" % stats.artists)
        print "Entry = \"%s\" { Actions = \"Exec echo 'albums'\" }" % ("Albums in DB: %s" % stats.albums)
        print "Entry = \"%s\" { Actions = \"Exec echo 'songs'\" }" % ("Songs in DB: %s" % stats.songs)
    print "}"
        print "Separator {}"
        print "Entry = \"Next\" { Actions = \"Exec mpc next\" }"
        print "Entry = \"Prev\" { Actions = \"Exec mpc prev\" }"
    if status['state'] == "pause":
        print "Entry = \"Play\" { Actions = \"Exec mpc play\" }"
    if status['state'] == "play":
        print "Entry = \"Pause\" { Actions = \"Exec mpc pause\" }"
        print "Entry = \"Stop\" { Actions = \"Exec mpc stop\" }"
        print "Separator {}"
        print "Entry = \"%s\" { Actions = \"Exec mpc %s\" }" % ('Toggle random %s' % (
            int(status.random) and '[On]' or '[Off]'), 'random')
        print "Entry = \"%s\" { Actions = \"Exec mpc %s\" }" % ('Toggle repeat %s' % (
            int(status.repeat) and '[On]' or '[Off]'), 'repeat')
        print "Entry = \"%s\" { Actions = \"Exec mpc %s\" }" % ('Toggle random %s' % (
            int(status.single) and '[On]' or '[Off]'), 'single')
        print "Entry = \"%s\" { Actions = \"Exec mpc %s\" }" % ('Toggle single %s' % (
            int(status.consume) and '[On]' or '[Off]'), 'consume')
        print "Separator {}"
        print "Submenu = \"%s\" {" % ("Volume: %s%%" % status.volume)
        print "Entry = \"%s\" { Actions = \"Exec mpc volume 100\" }" % ('[100%]')
        print "Entry = \"%s\" { Actions = \"Exec mpc volume 90\" }" % ('[90%]')
        print "Entry = \"%s\" { Actions = \"Exec mpc volume 80\" }" % ('[80%]')
        print "Entry = \"%s\" { Actions = \"Exec mpc volume 70\" }" % ('[70%]')
        print "Entry = \"%s\" { Actions = \"Exec mpc volume 60\" }" % ('[60%]')
        print "Entry = \"%s\" { Actions = \"Exec mpc volume 50\" }" % ('[50%]')
        print "Entry = \"%s\" { Actions = \"Exec mpc volume 40\" }" % ('[40%]')
        print "Entry = \"%s\" { Actions = \"Exec mpc volume 30\" }" % ('[30%]')
        print "Entry = \"%s\" { Actions = \"Exec mpc volume 20\" }" % ('[20%]')
        print "Entry = \"%s\" { Actions = \"Exec mpc volume 10\" }" % ('[10%]')
        print "Entry = \"%s\" { Actions = \"Exec mpc volume 0\" }" % ('[Mute]')
        print "}"
        print "Submenu = \"Playlist\" {"
    print "Entry = \"Clear\" { Actions = \"Exec mpc clear\" }"
        print "Separator {}"
        for entity in connect.lsinfo():
            if 'playlist' in entity:
                playlist = entity['playlist']
                print "Entry = \"%s\" { Actions = \"Exec mpc %s\" }" % (playlist, 'load %s' % playlist)
        print "}"
        print "Separator {}"
        print "Entry = \"%s\" { Actions = \"Exec mpc %s\" }" % ('Update Database','update')
        print "Entry = \"%s\" { Actions = \"Exec killall mpd\" }" % ('Kill MPD')
    print "}"

I use the following code to place that script in my ~/.pekwm/menu file:

Submenu = "MPD" {
     Entry { Actions = "Dynamic /home/YOURUSERNAME/.pekwm/scripts/mpd.py" }
}



I've got some more laying around. I host the good ones on my site at http://www.silverirc.com/kittykatt/inde … ge=scripts

Last edited by kittykatt (2010-04-04 07:14:29)


- [ My Blog ] | [ AUR Packages ] | [ My deviantART ] | [ screenFetch ] | [ SilverIRC ] -

Offline

#961 2010-04-04 07:09:51

dmz
Member
From: Sweden
Registered: 2008-08-27
Posts: 881
Website

Re: Post your handy self made command line utilities

mail
frontend to msmpt for quick mailing jobs

#!/usr/bin/perl
use strict;
#mail

my @details = @ARGV;
my $acc = "gmail";

&interactive if !@ARGV;

sub mail {
  my ($subject, $body, $rec) = @_;
  $rec = "foo\@bar.com" unless $rec;
  my $content = sprintf("Subject:$subject\n\n$body\n");
  system("printf \"$content\"|msmtp -a $acc \"$rec\"");
  print "Status: $?\n";
}
&mail(@details);

sub interactive {
  print "Subject: ";
  my $subject = <STDIN>;
  print "Body: ";
  my $body    = <STDIN>;
  print "Mail: ";
  my $rec     = <STDIN>;
  chomp($subject,$body,$rec);
  my $content = sprintf("Subject:$subject\n$body\n");
  system("printf \"$content\"|msmtp -a $acc \"$rec\"");
  print "Status: $?\n";
}

unpack
Unpacks movie to base_mountpoint/.temp, chmod it with 777 (havent found another solution to being able to see what movies I've seen - I KNOW this is bad, mkay wink, and removes the unpacked movie when I've watched it.

#!/bin/sh
# unpack script
chmod 777 $(pwd)
find . -iname '*.rar' -execdir rar x {} $(pwd | perl -pe 's/^((?:\/[^\/]+){2}\/).*/$1/').temp/ \;
mplayer -slave -input file=/home/scp1/.mplayer/fifo $(pwd | perl -pe 's/^((?:\/[^\/]+){2}\/).*/$1/').temp/* && rm $(pwd | perl -pe 's/^((?:\/[^\/]+){2}\/).*/$1/').temp/*
echo $(basename $(pwd)) >> /mnt/Movies_1/.seen

mpdftp
quickly ftp current artist and/or album to host

#!/bin/sh
ftphost="user@host.tld"
username="foo"
password="bar"
tempfile=$(mktemp)

echo -e "debug\n open -u $username,$password $ftphost" > $tempfile

if [ $# == 0 ]; then
        echo -e "Usage: $0 [OPTION]... 
        -a\t query artist
        -al\t query album"
fi

if [ "$1" == "-a" ]; then
        files=("$(mpc search artist "$2"|perl -pe 's/(.*\/.*\/).*/$1/')")
elif [ "$1" == "-al" ]; then
        files=("$(mpc search album "$2"|perl -pe 's/(.*\/.*\/).*/$1/')")
fi
for file in "${files[@]}"; do
        echo "$file"|perl -pe 's/^/mirror -R \/mnt\/Music_1\//g' >> $tempfile
done
lftp -f $tempfile

clfpretty
prettify clf-logs (apache,lighttpd etc) while tailing them

#!/usr/bin/perl 
use strict;
# clfpretty - prettity 
# prettify the Common Log Format accesslogs.

use File::Tail;

my $log     = $ARGV[0];
my $dgreen    = "\033[32;1m";
my $red        = "\033[31m";
my $lred    = "\033[31;1m";
my $blue    = "\033[34m";
my $dblue    = "\033[34;1m";
my $grey    = "\033[30;2m";
my $nc        = "\033[0m";
my $line    = "";
my $tail     = File::Tail->new(name=>$log, maxinterval=>3, adjustafter=>3, interval=>0, tail=>100);
while(defined($line=$tail->read)) {
    my $e = "(.+?)";
    $line =~ /^$e $e $e \[$e:$e $e\] "$e $e $e" $e $e/;

    my $ip        = $1;
    my $ref        = $2;
    my $name    = $3;
    my $date    = $4;
    my $time    = $5;
    my $gmt        = $6;
    my $request    = $7;
    my $file    = $8;
    my $ptcl    = $9;
    my $code    = $10;
    my $size    = $11;

    printf "%s %s %s %7s %s %s %s %s %s %s %s %-13s %s %80s %s\n",
            $dgreen,$10,$grey,$7,$nc,$3,$blue,$4, $red,$5,$dblue,$1,$lred,$8,$nc;

}

And this is just an POC, had fun labbing with mplayer's ability to use FIFO's
I'm using the numpad for mplayer bindings now, so I can control it from anywhere

#!/usr/bin/perl
use strict;
my $fifo = "/home/scp1/.mplayer/fifo";
my %commands = ('fs'    =>  'vo_fullscreen',
                'stop'  =>  'stop',
                'pause' =>  'pause',
                'soff'  =>  'mute 1',
                'son'   =>  'mute 0',
                'fstep' =>  'frame_step',
                'osd'   =>  'osd',
               );

if(!@ARGV) {
  foreach my $choice(sort(keys(%commands))) {
    printf("%6s %s\n", $choice, $commands{$choice});
  }
}

my $cmd = shift;
&talk($commands{$cmd});

sub talk {
  my $cmd = shift;
  open(FIFO,'>',$fifo) or die $!;
  print FIFO $cmd, "\n";
  close(FIFO);
}

mpdmon-full
Whenever song changes, we'll output the new filename

#!/usr/bin/perl 
use strict;
# mpdmon-full
# Copyright (C) trapd00r 2010
# This version of mpdmon outputs [time] artist/album/song whenever song
# changes, keeping a history of recently played tracks for reference.
# If you want a realtime alternative, you'll want;
# http://github.com/trapd00r/mpdmon-realtime

use Audio::MPD;
use List::Util qw(shuffle);
my $mpd = Audio::MPD->new;

sub monitor {
  my $np = "";
  my @c;
  for(my $i=0;$i<256;$i++) {
    push(@c, "\033[38;5;$i"."m");
  }
  while(1) {
    my $current = $mpd->current;
    my $output = $mpd->current->file;
    if(!$current) {
      $current = $c[1].'undef'.$c[0];
      $output = $current;
    }
    my $time1 = $mpd->status->time->sofar;
    my $time2 = $mpd->status->time->total;
    my @date  = localtime(time);

    if("$np" ne "$current") {
      $np = $current;
      my @rc = shuffle(@c);
      printf("%02s:%02s:%02s▕ $rc[0]%s$c[0]\n",
              $date[2], $date[0], $date[1], $output);
    }
    sleep 2;
  }
}

&monitor;

mpdmon-rt
And this does the same thing except that before a song change, we'll flush the buffer for the next print, keeping only one updated line of output. Could be used in conky/dzen/hardstatus line or whatever I guess.

#!/usr/bin/perl
use strict;
# mpdmon-realtime
# Copyright (C) trapd00r 2010
# This version of mpdmon shows a one-line string with playingtime/totaltime
# and the artist/album/title. Good for including in screens hardstatusline,
# conky, dzen2, your small shell up in the right corner or whatever.
# If you want to be able to see history, you'll want mpdmon-full;
# http://github.com/trapd00r/mpdmon-full

use Audio::MPD;
my $mpd = Audio::MPD->new;

sub monitor {
  my $np = "";
  while(1) {
    my $current = $mpd->current;
    my $output;
    if(!$current) {
      $output = 'undef';
      $current = 'undef';
    }
    else {
      $output = $mpd->current->artist . ' - ' . 
                $mpd->current->album  . ' - ' .
                $mpd->current->title;
    }
    my $time1 = $mpd->status->time->sofar;
    my $time2 = $mpd->status->time->total;
    
    $| = 1;
    printf(" [%s/%s]\r", $time1, $time2);
    if("$np" ne "$current") {
      $np = $current;
      printf("\t\t%s\r", $output);
    }
    sleep 1;
  }
}

&monitor;

Offline

#962 2010-04-04 07:17:25

kittykatt
Member
From: Missouri, USA
Registered: 2009-11-04
Posts: 260
Website

Re: Post your handy self made command line utilities

@dwm: Some nice scripts there! I like the MPD ones. I'm working on a MPD song change monitor myself at the moment.

Ah! Just found this one lying around. It's a script to output current disk usage in conky. Uses the Poky font, which can be found on my server. Download here.

#!/usr/bin/env python
import sys
import os
import subprocess

# root filesystem
print "${voffset -2}${color0}${font Poky:size=15}y${font}${color}${offset 6}${voffset -7}Root: ${font Liberation Sans:style=Bold:size=8}${color1}${fs_used_perc /}%${color}${font}"
print "${voffset 2}${color0}${fs_bar 4,20 /}${color}${offset 8}${voffset -2}F:${color2}${fs_free /}${color} U:${color2}${fs_used /}${color}"

# /home folder (if its a separate mount point)
if os.path.ismount("/home"):
    print "${voffset -2}${color0}${font Poky:size=15}y${font}${color}${offset 6}${voffset -7}Home: ${font Liberation Sans:style=Bold:size=8}${color1}${fs_used_perc /home}%${color}${font}"
    print "${voffset 2}${color0}${fs_bar 4,20 /home}${color}${offset 8}${voffset -2}F:${color2}${fs_free /home}${color} U:${color2}${fs_used /home}${color}"

# Add extra mounts that aren't / or /home and aren't in /media
# if os.path.ismount("/path/to/mount"):
#     print "${voffset -2}${color0}${font Poky:size=15}y${font}${color}${offset 6}${voffset -7}Music: ${font Liberation Sans:style=Bold:size=8}${color1}${fs_used_perc /path/to/mount}%${color}${font}"
#     print "${voffset 2}${color0}${fs_bar 4,20 /path/to/mount}${color}${offset 8}${voffset -2}F:${color2}${fs_free /path/to/mount}${color} U:${color2}${fs_used /path/to/mount}${color}"

# folder in /media
for device in os.listdir("/media/"):
    if (not device.startswith("cdrom")) and (os.path.ismount('/media/'+device)):
        print "${voffset -2}${color0}${font Poky:size=15}y${font}${color}${offset 6}${voffset -7}"+device.capitalize()+": ${font Liberation Sans:style=Bold:size=8}${color1}${fs_used_perc /media/"+device+"}%${color}${font}"
        print "${voffset 2}${color0}${fs_bar 4,20 /media/"+device+"}${color}${offset 8}${voffset -2}F:${color2}${fs_free /media/"+device+"}${color} U:${color2}${fs_used /media/"+device+"}${color}"

Include it in conky using "${execpi python /path/to/conkyHD.py}".

Non-Relevant: I loaded my website to copy the source of that script and saw one of my random BOFH excuses pop up that I thought I would share because it made me laugh...

"BOFH excuse #184: loop found in loop in redundant loopback"

Last edited by kittykatt (2010-04-04 07:24:10)


- [ My Blog ] | [ AUR Packages ] | [ My deviantART ] | [ screenFetch ] | [ SilverIRC ] -

Offline

#963 2010-04-04 08:32:22

gazj
Member
From: /home/gazj -> /uk/cambs
Registered: 2007-02-09
Posts: 681
Website

Re: Post your handy self made command line utilities

A collection of dmenu scripts

#!/bin/bash

#dmenu_i3
#Gazj 2010 garyjames82atgmaildotcom
#execute i3 commands using dmenu, history stored under ~/.i3/dmenu

if [ -f $HOME/.dmenurc ]; then
  . $HOME/.dmenurc
else
  DMENU='dmenu -i'
fi

GS=`cat ~/.i3/dmenu | $DMENU -p "i3 command:" $*`

if grep -q "$GS" "$HOME/.i3/dmenu" ; then
    echo already exists in history
else
    echo $GS >> ~/.i3/dmenu
fi

i3-msg $GS
#!/bin/bash

#dmenu_google
#Gazj 2010 garyjames82atgmaildotcom
#dmenu google search with history stored under ~/.ghist

if [ -f $HOME/.dmenurc ]; then
  . $HOME/.dmenurc
else
  DMENU='dmenu -i'
fi

GS=`cat ~/.gshist | $DMENU -p "google search" $*`

if grep -q "$GS" "$HOME/.gshist" ; then
    echo already exists in history
else
    echo $GS >> ~/.gshist
fi

firefox -new-window http://www.google.co.uk/search?q="$GS"
#!/bin/bash

#dmenu_mpd
#Gazj 2010 garyjames82atgmaildotcom (not strictly mine, modified from unknown source)
#dmenu mpd album add

if [ -f $HOME/.dmenurc ]; then
  . $HOME/.dmenurc
else
  DMENU='dmenu -i'
fi

cmd=$(mpc ls | $DMENU)

if [ -d ~/Music/"$cmd" ]; then
  mpc clear & mpc add "$cmd"
  mpc play
fi
#!/bin/bash
if [ -f $HOME/.dmenurc ]; then
  . $HOME/.dmenurc
else
  DMENU='dmenu -i'
fi

exe=`dmenu_path | $DMENU -p "Run" ${1+"$@"}` && exec $exe
#!/bin/bash

#dmenu_google
#Gazj 2010 garyjames82atgmaildotcom
#dmenu google I'm feeling lucky with history stored under ~/.ghist
#used in combination with the lucky script

if [ -f $HOME/.dmenurc ]; then
  . $HOME/.dmenurc
else
  DMENU='dmenu -i'
fi

GS=`cat ~/.gshist | $DMENU -p "Feeling lucky" $*`

if grep -q "$GS" "$HOME/.gshist" ; then
    echo already exists in history
else
    echo $GS >> ~/.gshist
fi

lucky $GS

depends on the following lucky script

#!/bin/bash

#lucky
#Gazj 2010 garyjames82atgmaildotcom
#Use Google I'm feeling lucky from the cli

term="$*"
[ -z "$term" ] && term="$(xclip -o)"
URL="http://www.google.com/search?btnI=I%27m+Feeling+Lucky&q=${term// /+}"
firefox -new-window "$URL" &>/dev/null &

Offline

#964 2010-04-04 09:06:24

Pajaro
Member
Registered: 2004-04-21
Posts: 884

Re: Post your handy self made command line utilities

make a backup of mysql, schema by schema and table by table, cerating a single file per table, and keeping untouched the timestamps on tables that have not been updated. I use it for incremental backups with hardlinks.

#!/bin/bash
MUSER="root"
MPASS="pass"
MHOST="localhost"
BACKUP=/root/mysqldump

### Get all databases name ###
DBS=$(mysql -u $MUSER -p"$MPASS" -h $MHOST -Bse 'show databases')
for db in $DBS; do
    ### Create dir for each databases, backup tables in individual files ###
    mkdir -p $BACKUP/$db

    for i in `echo "show tables" | mysql -u $MUSER -h $MHOST -p$MPASS $db|grep -v Tables_in_`; do
        FILE=$BACKUP/$db/$i.sql
        NEW_FILE=$BACKUP/$db/$i.sql.new
        echo -n $db.$i
        mysqldump --add-drop-table --allow-keywords -q -c -u $MUSER -h $MHOST -p$MPASS $db $i | sed 's/\-\-\ Dump\ completed.*//g' > $NEW_FILE
        DIFF=$(diff $FILE $NEW_FILE)
        if [ "$DIFF" == "" -a -f $FILE ]; then
            echo " ...unchanged"
            rm $NEW_FILE;
        else
            echo " ...UPDATED"
            mv $NEW_FILE $FILE
        fi
    done
done

Last edited by Pajaro (2010-04-04 09:06:37)

Offline

#965 2010-04-04 18:03:21

kittykatt
Member
From: Missouri, USA
Registered: 2009-11-04
Posts: 260
Website

Re: Post your handy self made command line utilities

This is a small, ugly, hacky Bash calculator I worked on last year that I just now found. Needs loads of improving and cleaning, if someone's up to it. big_smile

#!/bin/bash


# Temperature Conversions
if [ $1 == --temp ]
then
  # Celcius to Fahrenheit
  if [ $2 == C ]
  then
    for i in $3
    do
      echo "Celsius: " $i
      temp1=`echo "scale=3; 9/5" | bc`
      temp2=`echo "scale=3; $temp1 * $i" | bc`
      fah=`echo "scale=3; 32 + $temp2" | bc`
      echo "Fahrenheit: " $fah
    done
  fi
  # Fahrenheit to Celcius
  if [ $2 == F ]
  then
    for i in $3
    do
      echo "Fahrenheit: " $i
      temp1=`echo "scale=3; 5/9" | bc`
      temp2=`echo "scale=3; $i - 32" | bc`
      cel=`echo "scale=3; $temp1 * $temp2" | bc`
      echo "Celsius: " $cel
    done
  fi
fi

# Conversions
if [ $1 == --conv ]
then
  # Decimal to Hexadecimal
  if [ $2 == -dh ]
  then
    for i in $3
    do
      echo "Decimal Value: " $i
      dh=`echo "obase=16;$i" | bc`
      echo "Hexadecimal Value: " $dh
    done
  fi
  # Hexadecimal to Decimal
  if [ $2 == -hd ]
  then
    for i in $3
    do
      echo "Hexadecimal Value: " $i
      hd=`echo "ibase=16;obase=A;$i" | bc`
      echo "Decimal Value: " $hd
    done
  fi
  # Binary to Decimal
  if [ $2 == -bd ]
  then
    for i in $3
    do
      echo "Binary Value: " $i
      bd=`echo "ibase=2;obase=A;$i" | bc`
      echo "Decimal Value: " $bd
    done
  fi
  # Decimal to Binary
  if [ $2 == -db ]
  then
    for i in $3
    do
      echo "Decimal Value: " $i
      db=`echo "obase=2;$i" | bc`
      echo "Binary Value: " $db
    done
  fi
fi

  # Simple Math Operations
if [[ $1 != --* ]]
then
  echo "scale=4; $1" | bc ;exit
fi

- [ My Blog ] | [ AUR Packages ] | [ My deviantART ] | [ screenFetch ] | [ SilverIRC ] -

Offline

#966 2010-04-05 06:26:31

kittykatt
Member
From: Missouri, USA
Registered: 2009-11-04
Posts: 260
Website

Re: Post your handy self made command line utilities

Just wrote up a small libnotify interface for MPD using mpc, though it could be modified quite easily to fit most of the other console music players. You have to change the naming layout of your cover-art and the folder in which your cover-art is stored. This doesn't currently support downloading the cover-art when it's not found. Feel free to

I used the bashnotify daemon written by brisbin33. Thread can be found here.

Screenshot
mpdlibnotifytest2.thumb.png

Dependencies: imagemagick, mpc, mpd, libnotify

#!/bin/bash
#
# pbrisbin 2010
#
# http://pbrisbin.com:8080/bin/bashnotify
#
# modify the config after it's written!
#
# modified by KittyKatt (kittykatt@archlinux.us)
#                       (http://www.silverirc.com/kittykatt/) 2010
#
###

### utilities {{{
message() { echo 'usage: bashnotify [start|stop|restart]'; exit 1; }

logger() { echo "$(date +'[ %d %b %Y %H:%M ]') :: $*" | tee -a "$log"; }

errorout() { logger "error: $*"; exit 1; }

# }}}

### write config file {{{
write_config() {
  if [ ! -f "$config" ]; then

    logger "NOTE: writing example config to $config..."
    logger "you should edit this file before starting the deamon"

    cat > "$config" << EOF
#!/bin/bash
#
# bashnotify config - any valid bash is allowed, you
# only need to define handle_event() for the deamon
# to run properly
#
# \$mydir exists, and it's value is $mydir
#
###

###
#
# the actual handle_event() definition
#
# this is the only requirement for a valid config
#
###

handle_event() {
  coverDir="$HOME/.covers/"
  tmpCover="/tmp/cover"
  mpdPID=$(pidof mpd)

  if [[ -z $mpdPID ]]; then
    echo "MPD is not currently running!"
    exit
  else
    mpcAlbum=$(mpc --format '%album%' | head -1)
    mpcArtist=$(mpc --format '%artist%' | head -1)
    mpcTitle=$(mpc --format '%title%' | head -1)
    mpcLength=$(mpc --format '%time%' | head -1)
    if [ -f "/tmp/cover" ]; then rm /tmp/cover; fi
    if [ -f "$coverDir/$mpcAlbum" ]; then
      cp "$coverDir/$mpcAlbum" /tmp/cover
      mogrify -resize 65x65 /tmp/cover
      [[ "${#mpcArtist}" -gt "21" ]] && mpcArtist=$(echo "`echo $mpcArtist | head -1 | cut -c1-20`...")
      [[ "${#mpcTitle}" -gt "31" ]] && mpcTitle=$(echo "`echo $mpcTitle | head -1 | cut -c1-30`...")
      [[ "${#mpcAlbum}" -gt "31" ]] && mpcAlbum=$(echo "`echo $mpcAlbum | head -1 | cut -c1-30`...")
      [[ -z $mpcArtist ]] && mpcAlbum="Could not find Artist Name"
      [[ -z $mpcTitle ]] && mpcAlbum="Could not find Song Title"
      [[ -z $mpcAlbum ]] && mpcAlbum="Could not find Album Name"
      [[ -z $mpcLength ]] && mpcAlbum="Could not find Song Length"
      notify-send --expire-time=2000 -i "/tmp/cover" "MPD Notification" "`echo "${mpcTitle}"; echo "By: ${mpcArtist}"; echo "From:  ${mpcAlbum}"; echo "Length: ${mpcLength}"`"
    else
      notify-send --expire-time=2000 "MPD Notification" "`echo "${mpcTitle}"; echo "By: ${mpcArtist}"; echo "From:  ${mpcAlbum}"; echo "Length: ${mpcLength}"`"
    fi
  fi
}

EOF

  exit 0

  fi

  . "$config"

  type -p handle_event || errorout 'handle_event() not defined, check your config'
}

# }}}

### start/stop deamon {{{
start_daemon() {
  [ -f "$pid" ] && errorout "file found at $pid, daemon already running?"

  # start listening in background
  ( while read -r; do
      handle_event
    done < <(mpc idleloop player) ) &

  echo $! > "$pid"
}

stop_daemon() {
  if [ -f "$pid" ]; then
    kill $(cat "$pid") || errorout 'error stopping daemon'

    rm "$pid"
  fi
}

# }}}

### constants

mydir="$XDG_CONFIG_HOME/mpdnotify/"

config="$mydir/config"

pid="$mydir/pid"

log="$mydir/log"

pipe="$mydir/pipe"

### run it

if [ ! -d "$mydir" ]; then
  mkdir -p "$mydir" || errorout "unable to create my dir $mydir"
fi

write_config

case "$1" in
  start)   start_daemon                       ;;
  stop)    stop_daemon                        ;;
  restart) stop_daemon; sleep 3; start_daemon ;;
  *)       message                            ;;
esac

I modified brisbin33's script to write MY original config when none is found instead of the one specified before. This gets rid of the need for a separate config file to be posted here, as the script creates it. I've also changed where it creates it to $XDG_CONFIG_HOME, which for me at least is ~/.config on this install.

Thanks for the template brisbin33. wink

Hosted at http://www.silverirc.com/kittykatt/inde … =mpdnotify

Last edited by kittykatt (2010-04-05 14:30:39)


- [ My Blog ] | [ AUR Packages ] | [ My deviantART ] | [ screenFetch ] | [ SilverIRC ] -

Offline

#967 2010-04-05 13:32:16

brisbin33
Member
From: boston, ma
Registered: 2008-07-24
Posts: 1,796
Website

Re: Post your handy self made command line utilities

kittykatt, nicely done.  just be careful, in writing your config file you have:

# $mydir exists, and it's value is /home/kittykatt/.bashnotify

will result in:

# /home/kittykatt/.bashnotify exists, and its value is /home/kittykatt/.bashnotify

as $mydir will be evaluated.  i think in the original i had this:

# \$mydir exists, and its value is $mydir

also, i rewrote your calculator a bit if you don't mind:

#!/bin/bash
#
# thanks kittykatt
#
###

# usage
message() {
  echo 'usage: calculater [option] <expression>'
  echo
  echo '  evaulate <expression>'
  echo
  echo '  options:'
  echo '        -cf   convert the result from celsius to fahrenheit'
  echo '        -fc   convert the result from fahrenheit to celsius'
  echo '        -dh   convert the result from decimal to hex'
  echo '        -hd   convert the result from hex to decimal'
  echo '        -bd   convert the result from binary to decimal'
  echo '        -db   convert the result from decimal to binary'
  echo

  exit 1
}

# conversion functions
c2f() { echo "scale=3; (9/5 * ($*)) + 32" | bc; } # C to F
f2c() { echo "scale=3; 5/9 * (($*) - 32)" | bc; } # F to C
d2h() { echo "obase=16; $*" | bc; }               # decimal to hex
h2d() { echo "ibase=16; obase=A; $*" | bc; }      # hex to decimal
b2d() { echo "ibase=2; obase=A; $*" | bc; }       # binary to decimal
d2b() { echo "obase=2; $*" | bc; }                # decimal to binary
cal() { echo "scale=4; $*" | bc; }                # general arithmetic

# check input, do convert, and quit
doit() {
  local conv="$1"; shift

  [ -n "$*" ] && $conv $* || message

  exit $?
}

# we need bc
which bc &>/dev/null || exit 1

# getopts
while [ -n "$1" ]; do
  case "$1" in
    -cf) shift; doit c2f $* ;;
    -fc) shift; doit f2c $* ;;
    -dh) shift; doit d2h $* ;;
    -hd) shift; doit h2d $* ;;
    -bd) shift; doit b2d $* ;;
    -db) shift; doit d2b $* ;;
    -*)  message            ;;
    *)   break              ;;
  esac
done

# if we're here just calculate
cal "$*"

Offline

#968 2010-04-05 14:15:10

kittykatt
Member
From: Missouri, USA
Registered: 2009-11-04
Posts: 260
Website

Re: Post your handy self made command line utilities

brisbin33 wrote:

kittykatt, nicely done.  just be careful, in writing your config file you have:

# $mydir exists, and it's value is /home/kittykatt/.bashnotify

will result in:

# /home/kittykatt/.bashnotify exists, and its value is /home/kittykatt/.bashnotify

as $mydir will be evaluated.  i think in the original i had this:

# \$mydir exists, and its value is $mydir

Great! Yeah, I modified the original config quite a bit and didn't realize until later a couple of things that you did. Then, I promptly forgot them again while posting this. >.< I'll tweak the original to include that. Did you like my truncating? wink That was a new thing for me.

Edit: Funny, how I found that in the RIGHT syntax in my personal version. I must have fixed that and just forgot to update it in this post before submitting it.

Also, do you think we might hack something together to act on certain events, like hooks? Because when I do something like "mpc clear" in a terminal, I get this huge output in a notification and it's quite annoying, as I do that quite often. I'd like to only show this notification on certain events (next, previous, play, pause).

brisbin33 wrote:

also, i rewrote your calculator a bit if you don't mind:

AWESOME. /me ganks it

Last edited by kittykatt (2010-04-05 14:30:03)


- [ My Blog ] | [ AUR Packages ] | [ My deviantART ] | [ screenFetch ] | [ SilverIRC ] -

Offline

#969 2010-04-05 16:08:41

brisbin33
Member
From: boston, ma
Registered: 2008-07-24
Posts: 1,796
Website

Re: Post your handy self made command line utilities

kittykatt wrote:

Did you like my truncating?  That was a new thing for me.

I do! though, i'm sorry to tell you this:

//blue/0/~/ mpdArtist='Some really really long artist name omg its so long'
//blue/0/~/ echo "${mpdArtist:0:22}..."
Some really really lon...

wink

kittykatt wrote:

Also, do you think we might hack something together to act on certain events, like hooks? Because when I do something like "mpc clear" in a terminal, I get this huge output in a notification and it's quite annoying, as I do that quite often. I'd like to only show this notification on certain events (next, previous, play, pause)t

i think this is a limitation of mpc itself.  the idleloop functionality is a bit lacking.  `mpc idleloop player` is as specific as i could get (hoping it only threw output on track changes or play/pause type events).  if you just do `mpc idleloop` you can see what it outputs for what events.  this output would be the only means of calling various hooks on various events (or choosing to ignore/act).  but like i said, it appears to be a bit lacking and very nonspecific.

Last edited by brisbin33 (2010-04-05 16:12:16)

Offline

#970 2010-04-05 17:01:19

kittykatt
Member
From: Missouri, USA
Registered: 2009-11-04
Posts: 260
Website

Re: Post your handy self made command line utilities

brisbin33 wrote:

I do! though, i'm sorry to tell you this:

//blue/0/~/ mpdArtist='Some really really long artist name omg its so long'
//blue/0/~/ echo "${mpdArtist:0:22}..."
Some really really lon...

You know what the sad part is? I already knew that, but I completely forgot about using that. -goes about shortening code-

brisbin33 wrote:

i think this is a limitation of mpc itself.  the idleloop functionality is a bit lacking.  `mpc idleloop player` is as specific as i could get (hoping it only threw output on track changes or play/pause type events).  if you just do `mpc idleloop` you can see what it outputs for what events.  this output would be the only means of calling various hooks on various events (or choosing to ignore/act).  but like i said, it appears to be a bit lacking and very nonspecific.

I'll have to have a look at that in a few minutes. I think if it throws out at least a couple of unique statuses, I can improve the functionality of this tenfold.

Edit: BUH. Yeah, idleloop is very bare bones. Hm. Gotta figure another way around this one....

Last edited by kittykatt (2010-04-05 20:54:04)


- [ My Blog ] | [ AUR Packages ] | [ My deviantART ] | [ screenFetch ] | [ SilverIRC ] -

Offline

#971 2010-04-07 01:49:45

markp1989
Member
Registered: 2008-10-05
Posts: 431

Re: Post your handy self made command line utilities

uses conkyeEmail  to check my gmail and then uses xbmc-send notifications to tell me, i added some checking against the last run so its doesnt give repeated notifications, i have it running every 20min via cron.

#!/bin/bash
emails=$(conkyEmail --servertype=IMAP  --servername=imap.gmail.com --ssl --username=*** --password=***)
emaillog=~/.email.log

if cat $emaillog | grep -q $emails ; then
echo no new emails since last run
else
xbmc-send -a "Notification([Gmail], You have $emails new message(s))"
fi

echo $emails > $emaillog

Desktop: E8400@4ghz - DFI Lanparty JR P45-T2RS - 4gb ddr2 800 - 30gb OCZ Vertex - Geforce 8800 GTS - 2*19" LCD
Server/Media Zotac GeForce 9300-ITX I-E - E5200 - 4gb Ram - 2* ecogreen F2 1.5tb - 1* wd green 500gb - PicoPSU 150xt - rtorrent - xbmc - ipazzport remote - 42" LCD

Offline

#972 2010-04-07 15:11:35

alexanderte
Member
From: Norway
Registered: 2010-01-20
Posts: 11
Website

Re: Post your handy self made command line utilities

I store my passwords in a key–value pair file like this:

key value
key value
key value

I've written this little script that sends a password of a given key to the clipboard. You can then enter "get-password.sh twitter". Browsers usually remember this, but it has happened a few times that I've lost them. "chown root" the passwords file and change its mode to 600.

#!/bin/sh
sudo cat ~/path/to/password/file | grep $* | cut -d ' ' -f 2 | tr -d '\n' | xclip

Please give me some feedback if you notice any security issues with this script. Hashing would make it more secure, but I'm not good at that stuff.

Offline

#973 2010-04-07 15:25:53

GraveyardPC
Member
Registered: 2008-11-29
Posts: 99

Re: Post your handy self made command line utilities

alexanderte wrote:

Please give me some feedback if you notice any security issues with this script. Hashing would make it more secure, but I'm not good at that stuff.

Hashing wouldn't really help in this situation, but a simple encryption system might be good. Like create an encrypted password vault using encfs or gpg. Here's an example that might help. Keeping that file in plain text, even if it's owned by root, isn't a good idea. You'll have to remember a single master password, but it's better than plain text.

Last edited by GraveyardPC (2010-04-07 15:29:10)

Offline

#974 2010-04-07 15:50:24

Daenyth
Forum Fellow
From: Boston, MA
Registered: 2008-02-24
Posts: 1,244

Re: Post your handy self made command line utilities

Make a wrapper script around it that invokes gpg

Offline

#975 2010-04-07 16:08:17

alexanderte
Member
From: Norway
Registered: 2010-01-20
Posts: 11
Website

Re: Post your handy self made command line utilities

GraveyardPC wrote:
alexanderte wrote:

Please give me some feedback if you notice any security issues with this script. Hashing would make it more secure, but I'm not good at that stuff.

Hashing wouldn't really help in this situation, but a simple encryption system might be good. Like create an encrypted password vault using encfs or gpg. Here's an example that might help. Keeping that file in plain text, even if it's owned by root, isn't a good idea. You'll have to remember a single master password, but it's better than plain text.

Cool stuff. I don't have time to look at it right now, but that would be perfect.

Offline

Board footer

Powered by FluxBB