package Vote; # command = vote use strict; use warnings; use Storable; # Tableau des votes # On les stocke de telle manière : # "#chan id" --> "chan" --------> chan/reply name # |----> "id" ----------> vote id # |----> "added date" --> timestamp # |----> "question" ----> yes/no question to answer # |----> "voters" ------> [user@host voters] # |----> "results" -----> ("yes" => nb, "no" => nb) # `----> "author" ------> user@host # ... our %votes = (); # Tableau des derniers votes ajoutés # Format : # "chan1" --> dernier vote id # ... our %last_added = (); # ### # vote_main # Permet de gérer des votes # # Usage : # !vote new <question> # !vote del <id> # !vote list [#chan] # !vote see <id> # !vote [<id>] {yes|no} # ### sub vote_main { my ($conn, $event, $reply_to, $ref_params) = @_; # Don't use in query, only on chans unless(substr($reply_to, 0, 1) eq '#') { $::displayer->sendto($reply_to, "Can't operate in query, sorry."); return 1; } if(defined($ref_params->[0]) && $ref_params->[0]) { if($ref_params->[0] eq 'new') { return 1 if(new_vote($conn, $event, $reply_to, $ref_params)); } elsif($ref_params->[0] =~ m'del(ete)?|remove') { return 1 if(del_vote($conn, $event, $reply_to, $ref_params)); } elsif($ref_params->[0] eq 'list') { return 1 if(list_votes($conn, $event, $reply_to, $ref_params)); } elsif($ref_params->[0] eq 'see') { return 1 if(see_vote($conn, $event, $reply_to, $ref_params)); } else { return 1 if(vote($conn, $event, $reply_to, $ref_params)); } } $ref_params->[0] = 'vote'; Help::help_main($conn, $event, $reply_to, $ref_params); } # Fin vote_main # ### # new_vote # Enregistre un nouveau vote # ### sub new_vote { my ($conn, $event, $reply_to, $ref_params) = @_; # On enlève le 'new' shift @{$ref_params}; unless(defined($ref_params->[0]) && $ref_params->[0]) { $::displayer->sendto($reply_to, "Didn't find your question."); return 0; } my $lc_reply_to = lc $reply_to; my $id = get_next_id($lc_reply_to); my (undef, $vote_id) = split / +/, $id; my $question = join(' ', @{$ref_params}); my $full_user = (lc $event->{'nick'}).'!'.(lc $event->{'userhost'}); my %new_vote = ( 'chan' => $lc_reply_to, 'id' => $vote_id, 'added date' => time(), 'question' => $question, 'voters' => [], 'results' => { 'yes' => 0, 'no' => 0 }, 'author' => $full_user ); $votes{$id} = \%new_vote; $last_added{$lc_reply_to} = $vote_id; my $add_date = localtime($votes{$id}->{'added date'}); $::displayer->sendto($reply_to, "Vote \x0304[\x0303id #$vote_id\x0304]\x03 added on $add_date"); $::logger->info("New vote: '$question', id='$id', added by $full_user"); return 1; } # Fin new_vote # ### # del_vote # Supprime un vote # ### sub del_vote { my ($conn, $event, $reply_to, $ref_params) = @_; # On enlève le 'del' ou autre shift @{$ref_params}; unless(defined($ref_params->[0])) { $::displayer->sendto($reply_to, "Didn't find the id."); return 0; } my $lc_reply_to = lc $reply_to; my $vote_id = $ref_params->[0]; if($ref_params->[0] =~ /^#?(\d+)$/) { $vote_id = $1; } my $id = $lc_reply_to.' '.$vote_id; my $full_user = (lc $event->{'nick'}).'!'.(lc $event->{'userhost'}); # On vérifie que c'est bien l'auteur qui demande la suppression du vote if(defined($votes{$id}) && $votes{$id}->{'author'} eq $full_user) { return real_del_vote($id, $lc_reply_to); } $::displayer->sendto($reply_to, "Can't find this vote on this chan or you're not authorized."); return 1; } # Fin del_vote # ### # real_del_vote # S'occupe de vraiment supprimer le vote et d'émettre le résultat à l'utilisateur # ### sub real_del_vote { my ($id, $reply_to) = @_; my $question = $votes{$id}->{'question'}; my $yes = $votes{$id}->{'results'}->{'yes'}; my $no = $votes{$id}->{'results'}->{'no'}; delete $votes{$id}; $::displayer->sendto($reply_to, "Vote for '$question' deleted"); $::displayer->sendto($reply_to, "Final results: yes = \x0303$yes\x03 - no = \x0304$no\x03"); $::logger->info("Vote removed: '$question', id='$id'. Results: yes=$yes, no=$no"); return 1; } # Fin real_del_vote # ### # list_votes # Liste les votes en cours sur le chan actuel # ### sub list_votes { my ($conn, $event, $reply_to, $ref_params) = @_; shift @{$ref_params}; my $to_check_chan = lc $reply_to; if(defined($ref_params->[0]) && substr($ref_params->[0], 0, 1) eq '#') { $to_check_chan = lc $ref_params->[0]; } my %current_votes = (); foreach my $id (sort keys %votes) { if($votes{$id}->{'chan'} eq $to_check_chan) { $current_votes{$id} = $votes{$id}; } } my @curr_votes = sort keys %current_votes; my $msg = ""; if(@curr_votes) { foreach my $id (@curr_votes) { $msg .= "\x0307{#" . $votes{$id}->{'id'} . "\x03"; $msg .= " Added by \x0304" . $votes{$id}->{'author'}; $msg .= "on \x0302" . localtime($votes{$id}->{'added date'}); $msg .= "\x0307}\x03 "; } } else { $msg = "There's currently no vote on $to_check_chan"; } $::displayer->sendto($reply_to, $msg); return 1; } # Fin list_votes # ### # see_vote # Affiche un vote particulier # ### sub see_vote { my ($conn, $event, $reply_to, $ref_params) = @_; # On enlève le 'see' shift @{$ref_params}; unless(defined($ref_params->[0])) { $::displayer->sendto($reply_to, "Didn't find the id."); return 0; } my $lc_reply_to = lc $reply_to; my $vote_id = $ref_params->[0]; my $id = $lc_reply_to." ".$vote_id; # On vérifie que le vote existe if(defined($votes{$id})) { my $msg = "Vote \x0304[\x0303id #$vote_id\x0304]\x03: "; $msg .= $votes{$id}->{'question'}; $msg .= " -- \x0302Added on " . localtime($votes{$id}->{'added date'}); $::displayer->sendto($reply_to, $msg); # Public/private? # if(lc $event->{'nick'} eq $votes{$id}->{'author'}) { my $yes = $votes{$id}->{'results'}->{'yes'}; my $no = $votes{$id}->{'results'}->{'no'}; $msg = "Current results: yes = \x0303$yes\x03 - no = \x0304$no\x03"; $::displayer->sendto($reply_to, $msg); } } else { $::displayer->sendto($reply_to, "Can't find this vote. It has either expired or been deleted."); } return 1; } # Fin see_vote # ### # vote # Enregistre le vote d'un votant # ### sub vote { my ($conn, $event, $reply_to, $ref_params) = @_; my $lc_reply_to = lc $reply_to; my $vote_id = $last_added{$lc_reply_to}; my $voter = $event->{'userhost'}; my $code = real_vote(join(' ', @{$ref_params}), $lc_reply_to, $vote_id, $voter); if($code == 0) { $::displayer->sendto($reply_to, "You've already answered to this vote \x0304[\x0303id #$vote_id\x0304]\x03"); } elsif($code == 1) { $::displayer->sendto($reply_to, "Your vote has been added."); } else { $::displayer->sendto($reply_to, "Can't find your answer."); } return 1; } # Fin vote # ### # real_vote # Enregistre vraiment le vote d'un votant # ### sub real_vote { my ($params, $chan, $vote_id, $voter) = @_; my $id = $chan." ".$vote_id; print Data::Dumper->Dump([$votes{$id}, $params], [qw(vote_struct params)]); if($params =~ /^(?:y(?:es)?|oui)$/i) { if(can_vote($id, $voter)) { $votes{$id}->{'results'}->{'yes'} += 1; push @{$votes{$id}->{'voters'}}, $voter; return 1; } return 0; } elsif($params =~ /^n(?:o(?:n)?)?$/i) { if(can_vote($id, $voter)) { $votes{$id}->{'results'}->{'no'} += 1; push @{$votes{$id}->{'voters'}}, $voter; return 1; } return 0; } elsif($params =~ /^#?(\d+)\s+(.+)$/i) { $vote_id = $1; my $answer = $2; $id = $chan." ".$vote_id; if(can_vote($id, $voter)) { return real_vote($answer, $chan, $vote_id, $voter); } } return -1; } # Fin real_vote # ### # can_vote # Vérifie qu'un utilisateur n'a pas déjà voté # ### sub can_vote { my ($id, $user) = @_; my $lc_user = lc $user; if(grep { $_ eq $lc_user } @{$votes{$id}->{'voters'}}) { return 0; } $::logger->info("$user can vote for $id"); return 1; } # ### # get_next_id # Renvoie le prochain vote_id libre # ### sub get_next_id { my $base = shift; my $cpt = 0; $cpt++ while(defined($votes{$base." ".$cpt})); return $base." ".$cpt; } # Fin get_next_id # ### # vote_help # Affiche l'aide de la commande '!vote' # ### sub vote_help { my ($conn, $event, $reply_to, $ref_params) = @_; $conn->privmsg($reply_to, "`".$Config::command_sign."vote new <question>` => " ."suggest a new vote"); $conn->privmsg($reply_to, "`".$Config::command_sign."vote {del|see} <id>` => " ."remove or see the vote with id <id>"); $conn->privmsg($reply_to, "`".$Config::command_sign."vote list [<#chan>]` => " ."list votes on <#chan> or the current one by default"); $conn->privmsg($reply_to, "`".$Config::command_sign."vote [<id>] {yes|no}` => " ."vote for the last created vote or the one specified by its <id>"); return 1; } # Fin vote_help # ### # vote_save # Sauvegarde les postit # ### sub vote_save { my (undef, $folder, $reloading) = @_; my $file_votes = $folder."/vote".$Config::suffixe.".sav"; store [\%votes, \%last_added], $file_votes; return 1; } # Fin vote_save # ### # vote_load # Remplis %postits au démarrage # ### sub vote_load { shift; # Pas besoin de la connexion au serveur my $folder = shift; my $file_votes = $folder."/vote".$Config::suffixe.".sav"; my $file_content = retrieve $file_votes; my ($ref_votes, $ref_last_added) = @{$file_content}; %votes = %{$ref_votes}; %last_added = %{$ref_last_added}; return 1; } # Fin vote_load 1; __END__