]> iEval git - app-devbot.git/blob - lib/App/Devbot.pm
b1c1b2478c31d0525e05bb57498ba6b68b45e5ec
[app-devbot.git] / lib / App / Devbot.pm
1 package App::Devbot;
2
3 use v5.14;
4 use strict;
5 use warnings;
6 our $VERSION = 0.001004;
7
8 use POE;
9 use POE::Component::IRC::State;
10 use POE::Component::IRC::Plugin::AutoJoin;
11 use POE::Component::IRC::Plugin::NickServID;
12
13 use File::Slurp qw/append_file/;
14 use IRC::Utils qw/parse_user/;
15
16 use Getopt::Long;
17 use POSIX qw/strftime/;
18 use Regexp::Common qw /net/;
19
20 ##################################################
21
22 my $nick='devbot';
23 my $password;
24 my $server='irc.oftc.net';
25 my $port=6697;
26 my $ssl=1;
27 my @channels;
28 my $trace=0;
29
30 my $log=1;
31 my $store_files=0;
32
33 GetOptions (
34 "nick=s" => \$nick,
35 "password=s" => \$password,
36 "server=s" => \$server,
37 "port=i" => \$port,
38 "ssl!" => \$ssl,
39 "channel=s" => \@channels,
40 "log!" => \$log,
41 "store-files!" => \$store_files,
42 "trace!" => \$trace,
43 );
44
45 my $irc;
46
47 sub mode_char {
48 my ($channel, $nick)=@_;
49 return '~' if $irc->is_channel_owner($channel, $nick);
50 return '&' if $irc->is_channel_admin($channel, $nick);
51 return '@' if $irc->is_channel_operator($channel, $nick);
52 return '%' if $irc->is_channel_halfop($channel, $nick);
53 return '+' if $irc->has_channel_voice($channel, $nick);
54 return ' '
55 }
56
57 sub log_event{
58 return unless $log;
59 my ($channel, @strings) = @_;
60 my $file=strftime '%F', localtime;
61 mkdir 'logs';
62 mkdir "logs/$channel";
63 append_file "logs/$channel/$file.txt", strftime ('%T ', localtime), @strings, "\n";
64 }
65
66 sub bot_start{
67 $irc->plugin_add (NickServID => POE::Component::IRC::Plugin::NickServID->new(Password => $password)) if defined $password;
68 $irc->plugin_add (AutoJoin => POE::Component::IRC::Plugin::AutoJoin->new(
69 Channels => \@channels,
70 RejoinOnKick => 1,
71 Rejoin_delay => 10,
72 Retry_when_banned => 60,
73 ));
74
75 $server = $1 if $server =~ /^($RE{net}{domain})$/;
76 $port = $1 if $port =~ /^([0-9]+)$/;
77
78 $irc->yield(register => "all");
79 $irc->yield(
80 connect => {
81 Nick => $nick,
82 Username => 'devbot',
83 Ircname => "devbot $VERSION",
84 Server => $server,
85 Port => $port,
86 UseSSL => $ssl,
87 }
88 );
89 }
90
91 sub on_public{
92 my ($fulluser, $channels, $message)=@_[ARG0, ARG1, ARG2];
93 my $nick=parse_user $fulluser;
94
95 for (@$channels) {
96 my $mode_char=mode_char $_, $nick;
97 log_event $_, "<$mode_char$nick> $message";
98 }
99 }
100
101 sub on_ctcp_action{
102 my ($fulluser, $channels, $message)=@_[ARG0, ARG1, ARG2];
103 my $nick=parse_user $fulluser;
104
105 log_event $_, "* $nick $message" for @$channels;
106 }
107
108 sub on_join{
109 my ($fulluser, $channel)=@_[ARG0, ARG1];
110 my ($nick, $user, $host)=parse_user $fulluser;
111
112 log_event $channel, "-!- $nick [$user\@$host] has joined $channel";
113 }
114
115 sub on_part{
116 my ($fulluser, $channel, $message)=@_[ARG0, ARG1, ARG2];
117 my ($nick, $user, $host)=parse_user $fulluser;
118
119 log_event $channel, "-!- $nick [$user\@$host] has left $channel [$message]";
120 }
121
122 sub on_kick{
123 my ($fulluser, $channel, $target, $message)=@_[ARG0, ARG1, ARG2, ARG3];
124 my $nick=parse_user $fulluser;
125
126 log_event $channel, "-!- $target was kicked from $channel by $nick [$message]";
127 }
128
129 sub on_mode{
130 my ($fulluser, $channel, @args)=@_[ARG0 .. $#_];
131 my $nick=parse_user $fulluser;
132 my $mode=join ' ', @args;
133
134 log_event $channel, "-!- mode/$channel [$mode] by $nick";
135 }
136
137 sub on_topic{
138 my ($fulluser, $channel, $topic)=@_[ARG0, ARG1, ARG2];
139 my $nick=parse_user $fulluser;
140
141 log_event $channel, "-!- $nick changed the topic of $channel to: $topic" if $topic;
142 log_event $channel, "-!- Topic unset by $nick on $channel" unless $topic;
143 }
144
145 sub on_nick{
146 my ($fulluser, $nick, $channels)=@_[ARG0, ARG1, ARG2];
147 my $oldnick=parse_user $fulluser;
148
149 log_event $_, "-!- $oldnick is now known as $nick" for @$channels;
150 }
151
152 sub on_quit{
153 my ($fulluser, $message, $channels)=@_[ARG0, ARG1, ARG2];
154 my ($nick, $user, $host)=parse_user $fulluser;
155
156 log_event $_, "-!- $nick [$user\@$host] has quit [$message]" for @$channels;
157 }
158
159 sub on_dcc_request{
160 return unless $store_files;
161 my ($fulluser, $type, $cookie, $name)=@_[ARG0, ARG1, ARG3, ARG4];
162 my $nick=parse_user $fulluser;
163 return unless $type eq 'SEND';
164 return unless $irc->nick_channels($nick);
165 return if $name =~ m,/,;
166
167 mkdir 'files';
168 $irc->yield(dcc_accept => $cookie, "files/$name");
169 }
170
171 sub run{
172 $irc=POE::Component::IRC::State->spawn();
173
174 POE::Session->create(
175 inline_states => {
176 _start => \&bot_start,
177 irc_public => \&on_public,
178 irc_ctcp_action => \&on_ctcp_action,
179 irc_join => \&on_join,
180 irc_part => \&on_part,
181 irc_kick => \&on_kick,
182 irc_mode => \&on_mode,
183 irc_topic => \&on_topic,
184 irc_nick => \&on_nick,
185 irc_quit => \&on_quit,
186 irc_dcc_request => \&on_dcc_request
187 },
188 options => {
189 trace => $trace
190 }
191 );
192
193 $poe_kernel->run();
194 }
195
196 1;
197
198 __END__
199
200 =head1 NAME
201
202 App::Devbot - IRC bot which helps development
203
204 =head1 SYNOPSIS
205
206 use App::Devbot;
207 App::Devbot->run;
208
209 =head1 DESCRIPTION
210
211 App::Devbot is an IRC bot which helps developers collaborate.
212
213 Right now, it only does channel logging and file storage. It might do more in the future.
214
215 =head1 OPTIONS
216
217 =over
218
219 =item B<--nick> I<nickname>
220
221 The nickname of devbot. Defaults to devbot.
222
223 =item B<--password> I<password>
224
225 If supplied, identify to NickServ with this password
226
227 =item B<--server> I<hostname>
228
229 The server to connect to. Defaults to irc.oftc.net.
230
231 =item B<--port> I<port>
232
233 The port to connect to. Defaults to 6697.
234
235 =item B<--ssl>, B<--no-ssl>
236
237 B<--ssl> enables connecting to the server with SSL, B<--no-ssl> disables this. Defaults to B<--ssl>.
238
239 =item B<--channel> I<channel>
240
241 Makes devbot connect to I<channel>. Can be supplied multiple times for multiple channels. Has no default value.
242
243 =item B<--log>, B<--no-log>
244
245 B<--log> enables logging events to 'logs/I<CHANNEL>/I<DATE>.txt'. B<--no-log> disables logging. Defaults to B<--log>.
246
247 =item B<--store-files>, B<--no-store-files>
248
249 B<--store-files> enables storing files received via DCC to 'files/I<FILENAME>'. Files are only accepted if the sender and devbot share a channel. B<Only use when all channel users are trusted>. B<--no-store-files> disables storing files. Defaults to <--no-store-files>.
250
251 =item B<--trace>, B<--no-trace>
252
253 B<--trace> enables POE::Component::IRC::State tracing. Useful for debugging. B<--no-trace> disables tracing. Defaults to B<--no-trace>.
254
255 =back
256
257 =head1 CAVEATS
258
259 As stated above, the B<--store-files> option should only be used on private channels where every user is trusted.
260
261 =head1 AUTHOR
262
263 Marius Gavrilescu, E<lt>marius@ieval.roE<gt>
264
265 =head1 COPYRIGHT AND LICENSE
266
267 Copyright (C) 2013 by Marius Gavrilescu
268
269 This library is free software; you can redistribute it and/or modify
270 it under the same terms as Perl itself, either Perl version 5.14.2 or,
271 at your option, any later version of Perl 5 you may have available.
This page took 0.073924 seconds and 4 git commands to generate.