יוצר: kenshin
גרסה: 1.0
תיאור: בוט נגד בוטנטים
הפעלה: כנסו למירק ולחצו במקלדת ALT + R, לחצו למעלה בצד שמאל File ואז New, תעתיקו את הסקריפט ותאשרו.
קוד: בחר הכל
alias load_help {
if (!$isfile($+(", $scriptdirk.help.txt, "))) {
var %s = $+(", $script, "), %d = $+(", $scriptdirk.help.txt\", ), %b
filter -cffn %s %d *( HELP SECTION )*
%b = $gettok($read(%d, 2), 1, 32)
filter -ffcr $+($calc(%b + 1), -, $lines(%s)) %s %d
if ($gettok($nopath($script), 2, 46) == ini) {
var %i 1, %s k.tmp.txt
write -c %s
while (%i <= $lines(k.help.txt)) {
$iif($regex($read(k.help.txt, %i), /^n[0-9]+=(.*)/), write %s $regml(1) )
inc %i
}
.remove k.help.txt
.rename %s k.help.txt
.remove %s
}
}
}
alias -l name return k.stack. $+ $1-
alias -l stack.deinit hfree -ws $name(*)
alias -l stack.create {
if ($1) {
var %name = $name($1)
if ($2 isnum) hmake %name $2
else hmake %name 10
}
}
alias -l stack.push {
var %name
if (-* iswm $1) {
if ($2) {
%name = $name($2)
if (!$hget(%name)) hmake %name 10
hadd $1 %name $3-
}
}
else {
if ($1) {
%name = $name($1)
if (!$hget(%name)) hmake %name 10
hadd %name $2-
}
}
}
alias -l stack.del {
if ($1) {
var %name $name($1)
if ($hget(%name)) {
if (!$2) {
hfree %name
}
else {
hdel %name $2-
}
}
}
}
alias -l stack.copy {
var %source = $name($1), %dest = $name($2), %i 1
$iif($hget(%dest), hfree %dest)
hmake %dest $hget(%source).size
while ($hget(%source, %i).item) {
hadd -m %dest $hget(%source, %i).item $hget(%source, $hget(%source, %i).item)
inc %i
}
}
alias -l stack.exist {
if ($1 && $2) {
var %name = $name($1)
if ($hget(%name, $2).item) return $true
}
return $false
}
alias -l stack.get {
if ($1 && $2) {
var %name = $name($1)
if ($hget(%name)) return $hget(%name, $2)
}
return $null
}
alias -l stack.unset {
if ($1 && $2) {
var %name = $name($1)
return $hget(%name, $2).unset
}
return $null
}
alias -l stack.count {
if ($1) {
var %name = $name($1)
if ($hget(%name)) return $hget(%name, 0).item
}
return 0
}
alias -l init_hash {
if ($hget($name(settings))) { return 1 }
else {
if ($exists($scriptdirk.main.hash)) {
stack.create settings 5
hload $name(settings) $+(", $scriptdirk.main.hash, ")
return 1
}
}
return $null
}
alias -l combo_box_load {
var %combo1 Message,Notice,Action,Ctcp
didtok $dname 9,35 44 %combo1
var %i 0, %combo2 ,
while (%i < 10) {
%combo2 = %combo2 $+ , $+ $mask(nick!ident@mirc.rocks.com, %i)
inc %i
}
didtok $dname 12,36 44 %combo2
if (!$1) {
did -c $dname 9,35 1
did -c $dname 12,36 1
}
else {
did -c $dname 9 $stack.get(settings, bytes_message_type)
did -c $dname 12 $stack.get(settings, bytes_bantype)
did -c $dname 35 $stack.get(settings, part_message_type)
did -c $dname 36 $stack.get(settings, part_bantype)
if (!$stack.get(settings, bytes_kickban)) did -b $dname 12
if (!$stack.get(settings, part_kickban)) did -b $dname 36
}
}
alias -l check_box_load {
did -b $dname 10,30
did -c $dname 10,30
if (!$1) {
did -c $dname 11,43
}
else {
if ($stack.get(settings, bytes_kickban)) did -c $dname 11
if ($stack.get(settings, part_kickban)) did -c $dname 43
if (!$stack.get(settings, bytes_kickban)) {
did -b $dname 18
}
if (!$stack.get(settings, part_kickban)) {
did -b $dname 44
}
}
}
alias -l edit_box_load {
if (!$1) {
did -o $dname 15,38,51 1 5
did -o $dname 25 1 Mass Flood Detected! (<type>)
did -o $dname 42 1 Revolving Door Flood Detected!
did -o $dname 37 1 15
did -o $dname 13 1 5000
did -o $dname 44,18 1 300
did -o $dname 17,45 1 180
did -o $dname 49 1 15
}
else {
did -o $dname 15 1 $stack.get(settings, bytes_duration)
did -o $dname 38 1 $stack.get(settings, part_duration)
did -o $dname 25 1 $stack.get(settings, bytes_message)
did -o $dname 42 1 $stack.get(settings, part_message)
did -o $dname 37 1 $stack.get(settings, max_part)
did -o $dname 13 1 $stack.get(settings, max_bytes)
did -o $dname 44 1 $stack.get(settings, part_kickban_duration)
did -o $dname 45 1 $stack.get(settings, part_mode_duration)
did -o $dname 17 1 $stack.get(settings, bytes_mode_duration)
did -o $dname 18 1 $stack.get(settings, bytes_kickban_duration)
did -o $dname 49 1 $stack.get(settings, max_lines)
did -o $dname 51 1 $stack.get(settings, lines_duration)
}
}
alias -l radio_box_load {
did -u $dname 31,39,32,41
if (!$1) {
did -c $dname 31,39
}
else {
$iif($stack.get(settings, bytes_inform_channel), did -c $dname 31, did -c $dname 32)
$iif($stack.get(settings, part_inform_channel), did -c $dname 39, did -c $dname 41)
if (!$stack.get(settings, bytes_inform_channel)) {
did -b $dname 9,33,25,26
}
if (!$stack.get(settings, part_inform_channel)) {
did -b $dname 35,42,33,26
}
}
}
alias -l save_settings {
if (!$hget($name(settings))) {
stack.create settings 5
}
stack.push settings max_lines $did(49).text
stack.push settings lines_duration $did(51).text
stack.push settings max_bytes $did(13).text
stack.push settings bytes_duration $did(15).text
stack.push settings bytes_inform_channel $did(31).state
stack.push settings bytes_message_type $did(9).sel
stack.push settings bytes_message $did(25).text
stack.push settings bytes_kickban $did(11).state
stack.push settings bytes_mode_duration $did(17).text
stack.push settings bytes_kickban_duration $did(18).text
stack.push settings bytes_bantype $did(12).sel
stack.push settings max_part $did(37).text
stack.push settings part_duration $did(38).text
stack.push settings part_inform_channel $did(39).state
stack.push settings part_message_type $did(35).sel
stack.push settings part_message $did(42).text
stack.push settings part_kickban $did(43).state
stack.push settings part_mode_duration $did(45).text
stack.push settings part_kickban_duration $did(44).text
stack.push settings part_bantype $did(36).sel
stack.push settings status $iif($did(28).text == on, 1, 0)
hsave $name(settings) $+(", $scriptdirk.main.hash, ")
}
alias -l dialog_load {
combo_box_load $1
check_box_load $1
radio_box_load $1
edit_box_load $1
if ($1) {
if (!$stack.get(settings, status)) {
did -o $dname 28 1 OFF
disable_dialog
}
}
}
alias -l disable_dialog did -b $dname 3,13,14,15,8,23,34,31,32,9,33,26,25,10,11,17,23,18,6,12,37,38,39,41,30,43,45,44,36,29,35,42,16,19,20,21,49,51,48,54,50,52
alias -l enable_dialog {
did -e $dname 3,13,14,15,8,23,34,31,32,9,33,26,25,11,17,18,6,12,37,38,39,41,43,45,44,36,29,35,42,16,19,20,21,49,51,54,23
if ((!$did(31).state) || ($did(32).state)) { did -b $dname 9,25,26,33 }
if (!$did(11).state) { did -b $dname 18,12 }
if ((!$did(39).state) || ($did(41).state)) { did -b $dname 35,42,26,33 }
if (!$did(43).state) { did -b $dname 44,36 }
}
alias -l update_data {
if (!$stack.get($+(#, .backup., $1), $+($1, _flood_detected))) {
stack.push $+(-u, $iif($stack.unset($+(#, ., $1), $iif($1 == part, $fulladdress, $nick)), $ifmatch, $stack.get(settings, $+($1, _duration)))) $+(#, ., $1) $iif($1 == part, $fulladdress, $nick) $calc($stack.get($+(#, ., $1), $iif($1 == part, $fulladdress, $nick)) + $iif($1 == lines || $1 == part, 1, $len($2-)))
stack.push $+(-u, $iif($stack.unset($+(#, ., $1), @total), $ifmatch, $stack.get(settings, $+($1, _duration)))) $+(#, ., $1) @total $calc($stack.get($+(#, ., $1), @total) + $iif($1 == lines || $1 == part, 1, $len($2-)))
}
else {
stack.push $+(#, .backup., $1) $nick $calc($stack.get($+(#, .backup., $1), $nick) + $iif($1 == part || $1 == lines, 1, $len($2-)))
stack.push $+(#, .backup., $1) @total $calc($stack.get($+(#, .backup., $1), @total) + $iif($1 == part || $1 == lines, 1, $len($2-)))
}
}
alias -l message_type {
if ($1 == 1) return msg
elseif ($1 == 2) return notice
elseif ($1 == 3) return action
elseif ($1 == 4) return ctcp
}
alias -l check_flood {
if ($stack.get($+(#, ., $1), @total) >= $stack.get(settings, $+(max_, $1))) return $true
return $false
}
alias -l flood_protect {
if (!$stack.get($+(#, .backup., $1), $+($1, _mode_change))) {
if ($stack.get($+(#, .backup., $1), $+($1, _mode_change_ok))) goto loop
stack.copy $+(#, ., $1) $+(#, .backup., $1)
stack.del $+(#, ., $1)
stack.push $+(#, .backup., $1) $+($1, _flood_detected) 1
stack.push $+(#, .backup., $1) $+($1, _mode_change) 1
if ( (($1 == bytes) || ($1 == lines)) && ((i !isin $gettok($chan(#).mode, 1, 32)) || (m !isin $gettok($chan(#).mode, 1, 32)))) {
mode # +im
}
elseif (($1 == part) && (i !isin $gettok($chan(#).mode, 1, 32))) {
mode # +i
}
else goto loop
}
else {
if ($stack.get($+(#, .backup., $1), $+($1, _mode_change_ok))) {
:loop
if ($stack.get(settings, $+($iif($1 == lines, bytes, $1), _inform_channel))) {
var %message = $stack.get(settings, $+($iif($1 == lines, bytes, $1), _message))
var %i = $regsub(%message, /(\w*?)(<type>)(\w*)/, $upper($1), %message)
$message_type($stack.get(settings, $+($iif($1 == lines, bytes, $1), _message_type))) # %message
}
stack.del $+(#, .backup., $1) $+($1, _mode_change)
stack.del $+(#, .backup., $1) $+($1, _mode_change_ok)
stack.del $+(#, .backup., $1) $+($1, _flood_detected)
if ($timer(bytes_flood_protection)) timerbytes_flood_protection off
if ($timer(lines_flood_protection)) timerlines_flood_protection off
if ($timer(part_flood_protection)) timerpart_flood_protection off
.timer 1 $stack.get(settings, $+($iif($1 == lines, bytes, $1), _mode_duration)) mode # -mi
if ($stack.get(settings, $+($iif($1 == lines, bytes, $1), _kickban))) {
var %i 1, %name = $+(#, .backup., $1), %bans, %kicks, %limit = $calc( ($stack.get($+(#, .backup., $1), @total) / $stack.count($+(#, .backup., $1))) - ($stack.get($+(#, .backup., $1), @total) * 0.01))
while (%i <= $stack.count($+(#, .backup., $1))) {
var %tmp = $hget($name(%name), %i).item
if (%tmp != @total) {
if ((part* iswm $1) || ($hget($name(%name), %tmp) >= %limit)) {
stack.push $+(#, .backup., $1) @tmp $addtok($stack.get($+(#, .backup., $1), @tmp), $gettok(%tmp, 1, 33), 32)
%bans = $addtok(%bans, $iif($1 == part, $mask( $hget( $name( $+(#, .backup.part) ), %i).item, $calc( $stack.get( settings, part_bantype ) - 1 ) ),$address( %tmp, $calc( $stack.get( settings, $+( $iif( $1 == lines, bytes, $1 ), _bantype ) ) - 1 ) ) ), 32)
if ($numtok(%bans, 32) == $modespl) {
mode # $+(+, $str(b, $modespl)) %bans
.timer 1 $stack.get(settings, $+($iif($1 == lines, bytes, $1), _kickban_duration)) mode # $+(-, $str(b, $numtok(%bans, 32))) %bans
%bans = $null
}
}
}
inc %i
}
if (%bans) {
mode # $+(+, $str(b, $numtok(%bans, 32))) %bans
.timer 1 $stack.get(settings, $+($iif($1 == lines, bytes, $1), _kickban_duration)) mode # $+(-, $str(b, $numtok(%bans, 32))) %bans
}
%i = 1
while (%i <= $numtok( $stack.get($+(#, .backup., $1), @tmp), 32)) {
var %tmp = $gettok($stack.get($+(#, .backup., $1), @tmp), %i, 32)
if (%tmp && %tmp != @tmp && %tmp ison # && %tmp !isop #) {
kick # %tmp $iif($1 == part, join/part, $1) flood
}
inc %i
}
}
hfree $name($+(#, .backup., $1))
}
}
}
; ==================( DIALOG ) =============================
dialog k.main {
title "Kenshin\'s Anti-Botnet Channel Protection v1.0"
size -1 -1 191 206
option dbu
tab "Mass Text Flood", 1, 9 7 174 177
text "Max Bytes ", 3, 23 38 28 8, tab 1
check "+im channel", 10, 22 124 40 10, tab 1
combo 9, 21 89 37 50, tab 1 size drop
combo 12, 67 166 100 50, tab 1 size drop
edit "", 13, 59 36 22 10, tab 1 center
edit "", 15, 110 37 17 10, tab 1 center
radio "yes", 31, 77 74 22 10, group tab 1
radio "no", 32, 106 74 22 10, tab 1
check "kick ban", 11, 22 138 34 10, tab 1
edit "", 17, 110 123 17 10, tab 1 center
edit "", 18, 110 138 17 10, tab 1 center
edit "", 25, 67 105 96 11, tab 1 autohs
text "Max Lines", 48, 23 49 25 8, tab 1
box "", 53, 23 58 151 4, tab 1
text "Action", 54, 21 64 25 8, tab 1
edit "", 49, 59 48 22 10, tab 1 center
edit "", 51, 110 48 17 10, tab 1 center
text "/", 50, 94 50 9 8, tab 1
text "sec", 52, 133 49 25 8, tab 1
tab "Mass Join Part Flood", 2
text "Max Part", 29, 23 38 34 8, tab 2
check "+i channel", 30, 22 124 50 10, tab 2
combo 35, 21 89 37 50, tab 2 size drop
combo 36, 67 166 100 50, tab 2 size drop
edit "", 37, 59 36 22 10, tab 2 center
edit "", 38, 110 37 17 10, tab 2 center
edit "", 42, 67 105 96 11, tab 2 autohs
radio "yes", 39, 77 74 22 10, group tab 2
radio "no", 41, 106 74 22 10, tab 2
check "kick ban", 43, 22 138 34 10, tab 2
edit "", 45, 110 123 17 10, tab 2 center
edit "", 44, 110 138 17 10, tab 2 center
box "", 24, 21 51 151 4, tab 2
text "Action", 23, 22 61 25 8, tab 2
text "Ban Type", 6, 23 166 25 8
text "sec", 8, 132 38 25 8
text "/", 14, 94 38 9 8
text "duration", 16, 83 125 25 8
text "duration", 19, 84 139 25 8
text "sec", 20, 131 124 25 8
text "sec", 21, 131 139 25 8
button "OK", 22, 94 189 37 12, ok
text "Message", 26, 38 106 25 8
button "Cancel", 27, 137 189 37 12, cancel
button "ON", 28, 40 189 17 12, flat
text "Inform Channel", 34, 21 76 38 8
text "Message Type", 33, 70 90 41 8
text "status", 47, 22 191 16 8
button "?", 46, 165 24 14 12
}
dialog k.h {
title "Hep"
size -1 -1 249 201
option dbu
edit "", 1, 5 4 239 178, read multi autovs hsbar vsbar
button "Exit", 2, 47 186 37 12, ok
link "kenshin@mircscripting.info", 3, 161 188 69 8
text "email :", 4, 141 188 18 8
}
on *:dialog:k.main:init:0:{
dialog_load $init_hash
load_help
}
on *:dialog:k.main:sclick:*:{
if ($did == 22) {
save_settings
if ($did(28).text == ON) {
.enable #k.protection
}
else {
.disable #k.protection
}
}
if ($did == 28) {
if ($did(28).text == ON) {
did -o $dname 28 1 OFF
disable_dialog $dialog($dname).tab
}
else {
did -o $dname 28 1 ON
enable_dialog
}
}
if ($did == 39) did -e $dname 35,42,33,26
if ($did == 41) did -b $dname 35,42,33,26
if ($did == 31) did -e $dname 9,25,26,33
if ($did == 32) did -b $dname 9,25,26,33
if ($did == 11) {
if ($did(11).state) did -e $dname 18,12
else did -b $dname 18,12
}
if ($did == 43) {
if ($did(43).state) did -e $dname 44,36
else did -b $dname 44,36
}
if ($did == 46) $dialog(k.h, k.h, -3)
}
on *:dialog:k.h:init:0:{
if (!$exists($scriptdirk.help.txt)) did -a $dname 1 HELP FILE MISSING!!!
else loadbuf -o $dname 1 k.help.txt
}
on *:dialog:k.h:sclick:3:run mailto:kenshin@mircscripting.info
; ==================( EVENTS ) =============================
#k.protection on
on @*:TEXT:*:#:{
update_data bytes $1-
update_data lines $1-
if ($check_flood(bytes)) flood_protect bytes
if ($check_flood(lines)) flood_protect lines
}
on @*:NOTICE:*:#:{
update_data bytes $1-
update_data lines $1-
if ($check_flood(bytes)) flood_protect bytes
if ($check_flood(lines)) flood_protect lines
}
on @*:ACTION:*:#:{
update_data bytes $1-
update_data lines $1-
if ($check_flood(bytes)) flood_protect bytes
if ($check_flood(lines)) flood_protect lines
}
on @*:CTCP:*:#:{
update_data bytes $1-
update_data lines $1-
if ($check_flood(bytes)) flood_protect bytes
if ($check_flood(lines)) flood_protect lines
}
on @*:PART:#:{
update_data part
if ($check_flood(part)) flood_protect part
}
on @*:MODE:#:{
if ($stack.get($+(#, .backup.bytes), bytes_mode_change)) {
stack.push $+(#, .backup.bytes) bytes_mode_change_ok 1
flood_protect bytes
}
else {
if ($stack.get($+(#, .backup.lines), lines_mode_change)) {
stack.push $+(#, .backup.lines) lines_mode_change_ok 1
flood_protect lines
}
}
if ($stack.get($+(#, .backup.part), part_mode_change)) {
stack.push $+(#, .backup.part) part_mode_change_ok 1
flood_protect part
}
}
on *:START:{
if ($exists($scriptdirk.main.hash)) {
stack.push settings 5
hload $name(settings) $+(", $scriptdirk.main.hash, ")
}
}
on *:LOAD:{
if ($version < 6.12) {
echo -a $+($chr(3), $color(Info text), *, $chr(32), This addon only works for mIRC version 6.12)
echo -a $+($chr(3), $color(Info text), *, $chr(32), Unloading script...)
linesep
timer 1 1 unload -rs $+(", $script, ")
}
else {
load_help
echo -a $+($chr(3), $color(Info text), *, $chr(32), Kenshin's Anti-Botnet Channel Flood Prection v1.0 Loaded!)
echo -a $+($chr(3), $color(Info text), *, $chr(32), You can use the menubar to modify settings.)
linesep
}
}
on *:UNLOAD:{
.remove k.help.txt
echo -a $+($chr(3), $color(info text), *, $chr(32), Anti-Botnet Channel Script Unloaded...)
linesep
}
#end
==================( MENU ) =============================
menu status,channel,query,menubar {
Anti-Botnet Channel Protection
.Settings:dialog -m k.main k.main
.-
.Uninstall:.unload -rs $+(", $script, ")
}
==================( HELP SECTION ) =============================
=========================================================================
Kenshin's Anti-Botnet Channel Protection v1.0
Author : kenshin
Bugs/Comments/Suggestions email : kenshin@mirscripting.info
Irc channel : #mircscripting (Undernet)
=========================================================================
Contents
1. Pros & Cons
2. Options Description
3. Final Notes
=========================================================================
PROS & CONS
=========================================================================
This script is intended to protect a channel from botnet floods. Let me emphasize
again BOTNET floods. Don't email me complaining it did not work telling "when a user
flooded my channel blah blah...". This is designed to stop 3 or more synchronized
bots flooding a channel. This script is not invincible all through out, as all scripts
are. Pros and Cons list below.
PROS:
* Fast reaction rate
* It uses hash tables all throughout. Thus all vars are resident in
the memory making it faster.
* Not a single global variable. Good for avoiding variable name
clashes between different scripts.
* Uses a *slightly smart* way to track down culprits
CONS:
* When the "Revolving Door" Join Part flood is triggered. It bans everyone
caught leaving the channel. (more on this in 'final notes section)
* Doesn't look for join floods alone.
=========================================================================
OPTIONS DESCRIPTION
=========================================================================
MAX TEXT FLOOD
Mass text flood section refers to ANY text which can be seen on channel. Text,
Notices, Action, Ctcp's are all in this category.
MAXBYTES Maximum number of characters that a channel
can allow for a certain amount of time. All text, notice, action,
ctcp texts are added and compared to this. if its greater or
equal, the script will take action on the channel.
MAXLINES Maximum number of lines a channel allows for a certain amount of
timer. This refers to all the lines (not per nick).
[ ACTION ]
INFORMCHANNEL Very self explanatory. If you want to inform everyone in the channel
about the flood, check this. Below are options available only if you
chose to inform the channel.
MESSAGETYPE If you chose to check INFORM CHANNEL, this tells
how will you relaythe message. Through msg,
notice, action, or ctcp.
MESSAGE The message you want to send to channel. <type>
is a variable u can use here. It returns the type
of flood detected. Whether "line", "bytes" or "part".
+mi CHANNEL This option is not allowed to be change as it is the heart of this
protection script.
DURATION How long until the script removes the +i mode
on the channel.
KICKBAN Kick and Ban Offenders.
DURATION How long until the script removes the ban.
BANTYPE The mask on how to ban offenders.
MAX JOIN PART FLOOD
MAXPART Maximum number of users leaving the channel for a certain amount of
time. All users leaving are logged and incremented. If its larger that or
equal to MAXPART, The script takes action.
DURATION The interval until the script resets the channel
part log.
+ii CHANNEL Locks the channel.
* All other options under Join / Part Flood works the same with the Max Text Flood section *
=========================================================================
FINAL NOTES
=========================================================================
A brief description on some of my ideas regarding this script.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* if you can't understand this part its ok, this is for others who knows a little scripting *
\"When the join / part flood is triggered. It bans everyone caught leaving the channel."
- Yes, this is one of the cons in this script. I could have scripted it look for the
actual culprits (which in fact i did). But i somehow imagined what if the
attacker has 200 or more bots in hand, synchronized it to join and
part the channel by turn without a single bot repeating the attack untill
all the bots had finised, then attack again. On the logs this will result to
all "bots" users only left the channel 1 time. That's why i decided to just ban
everyone who leaves the channel while the attack is going. Anyway it won't
take longer that 10 secs (depends on how you set it) probably not even
5 on a fast connection to stop this lamers.. However you still have the
option not to auto kick ban offenders, the script will just lock the channel
and you can do the cleaning manualy :>
\"Doesn't look for join floods alone."
- Yeah, the reason i didn't include this option (besides the fact that i'm lazy :P)
is that on Undernet there's a channel bot X that adjusts the maximum number
of users a channel can accomodate. So unless you got a lame auto-greeter
script, its harmless. Bah! still it's not a good excuse for me :x i'll consider
it on the next version i suppose.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Finally, i made this script to show lamers/script kiddies that this type of channel floods are
useless! I admire those who knows what they are doing, not those who just run the program
and poof! *showoff* ! :P
יש למצוא גם: pastebin.php?mode=view&s=30