OSDN Git Service

1d7b32708b6cd381361bbb2ab574d122afc099df
[mubot4fb/mubot4fb.git] / mubot4fb.pl
1 #!/usr/bin/perl
2 package Mubot4FB;
3
4 use strict;
5 use utf8;
6
7 use Facebook::Graph;
8 use Bot::BasicBot;
9 use base 'Bot::BasicBot';
10 use Encode;
11 use DBI;
12 use POSIX 'strftime';
13
14 use Data::Dumper;
15
16 my $mu_re = qr/^([^ ]+) (https?:\/\/[^ ]+) (.+)$/i;
17
18 sub fb_init {
19         my ($me) = @_;
20         my $fb = Facebook::Graph->new(app_id   => $me->{cfg}->{fb_app_id},
21                                       secret   => $me->{cfg}->{fb_app_secret},
22                                       postback => $me->{cfg}->{fb_postback_url});
23
24         my $res_token = $fb->request_access_token($me->{cfg}->{fb_access_code});
25         die 'token get error' if (!defined $res_token || !$res_token->response->is_success);
26
27         my $acts = $fb->fetch('me/accounts');
28         die 'can not get account list' if(!defined $acts || !$acts);
29
30         my $page_access_token = '';
31         foreach my $d (@{$acts->{data}}) {
32                 if ($d->{id} eq $me->{cfg}->{fb_page_id}) {
33                         $page_access_token = $d->{'access_token'};
34                 }
35         }
36         die 'can not get access tokenfor page_id=' . $me->{cfg}->{fb_page_id} if ($page_access_token eq '');
37
38         return $me->{fbo} = Facebook::Graph->new(access_token => $page_access_token);
39 }
40
41 sub db_init {
42         my ($me) = @_;
43         $me->{dbh} = DBI->connect('dbi:SQLite:dbname='.$me->{cfg}->{database}) || die $DBI::errstr;
44 }
45
46 sub publish {
47         my ($me, $text, $uri) = @_;
48
49         return $me->{fbo}->add_post($me->{cfg}->{fb_page_id})
50             ->set_message($text)
51             ->set_link_uri($uri)
52             ->publish()
53             ->as_hashref();
54 }
55
56 sub init {
57         my ($me) = @_;
58         $me->fb_init();
59         $me->db_init();
60 }
61
62 sub _check_dup {
63         my ($me, $args, $uri) = @_;
64
65         my $found = 0;
66
67         my $sth = $me->{dbh}->prepare('select * from posts where uri = ? order by post_time desc limit 1');
68         my $rv = $sth->execute($uri);
69         my $res = $sth->fetchrow_hashref;
70         if ($res) {
71                 if ($res->{post_time} < time() - 7 * 24 * 60 * 60) {
72                         $me->_response($args, 'だいぶ前に'.decode('utf-8', $res->{nick}).'が「'.decode('utf-8' $res->{prefix}).','.decode('utf-8', $res->{suffix}).'」とかいってたにゃー '.$me->{cfg}->{fb_page_url}.'posts/'.$res->{fb_post_id});
73                 } else {
74                         $me->_response($args, '既に'.decode('utf-8', $res->{nick}).'が「'.decode('utf-8', $res->{prefix}).','.decode('utf-8', $res->{suffix}).'」と'.strftime('%Y-%m-%d %H:%M:%S', localtime($res->{post_time})).'に言ってますよ? '.$me->{cfg}->{fb_page_url}.'posts/'.$res->{fb_post_id});
75                         $found = 1;
76                 }
77         }
78         $sth->finish;
79
80         return $found;
81 };
82
83 sub _db_insert {
84         my ($me, $db_args) = @_;
85
86         my $sth = $me->{dbh}->prepare("insert into posts (id, nick, fb_post_id, uri, prefix, suffix, post_time) values (null, ?, ?, ?, ?, ?, ?)");
87         my $rv = $sth->execute($db_args->{nick}, $db_args->{fb_post_id}, $db_args->{uri}, $db_args->{prefix}, $db_args->{suffix}, time);
88         $sth->finish;
89
90         return $rv;
91 }
92
93 sub _response {
94         my ($me, $args, $msg) = @_;
95
96         $me->say(channel => $args->{channel},
97                  body => $msg);
98 }
99
100 sub _add {
101         my ($me, $args)  =@_;
102         my $post_ok = 1;
103         my ($resp, $resp_msg);
104
105         if ($args->{body} =~ /$mu_re/) {
106                 my $prefix = $1;
107                 my $uri = $2;
108                 my $suffix = $3;
109                 my $text = $args->{who} . '曰く、'.$prefix.' '.$suffix;
110
111                 return 0 if ($me->_check_dup($args, $uri));
112
113                 eval{$resp = $me->publish($text, $uri)};
114                 if ($@) {
115                         $me->fb_init();
116                         eval{$resp = $me->publish($text, $uri)};
117                         $post_ok = 0 if ($@);
118                 }
119
120                 if ($post_ok) {
121                         my (undef, $post_id) = split(/_/, $resp->{id});
122                         $me->_db_insert({nick => $args->{who},
123                                          fb_post_id => $post_id,
124                                          uri => $uri,
125                                          prefix => $prefix,
126                                          suffix => $suffix});
127                         $resp_msg = 'うい: ' . $me->{cfg}->{fb_page_url} . 'posts/' . $post_id .' で登録';
128                 } else {
129                         $resp_msg = 'can not post to facebook';
130                 }
131
132                 return $resp_msg;
133         }
134         return 0;
135 }
136
137 sub _delete_prev {
138         my ($me, $args) = @_;
139
140         return _not_yet();
141 }
142
143 sub _delete_post_id {
144         my ($me, $args) = @_;
145
146         return _not_yet();
147 }
148
149 sub _delete {
150         my ($me, $id)  =@_;
151
152         return _not_yet();
153 }
154
155 sub _search {
156         my ($me, $id)  =@_;
157
158         return _not_yet();
159 }
160
161 sub _not_yet {
162         return 'まだ実装してないです';
163 }
164
165 sub said {
166         my ($me, $args) = @_;
167         my $resp_msg;
168
169         if ($args->{body} =~ /$mu_re/) {
170                 $resp_msg = $me->_add($args) unless ($1 eq 'deb');
171         } elsif ($args->{body} =~ /^ふみくん (.+)/) {
172                 my $cmd = $1;
173                 if ($cmd eq 'いまのなし') {
174                         $resp_msg = $me->_delete_prev($args);
175                 } elsif ($cmd =~ /削除 ([0-9]+)$/) {
176                         $resp_msg = $me->_delete($args, $1);
177                 } elsif ($cmd =~ /\?$/) {
178                         $resp_msg = $me->_search($args);
179                 } elsif ($cmd =~ /つぎ/) {
180                         $resp_msg = $me->_search_next($args);
181                 }
182         }
183
184         $me->_response($args, $resp_msg) if ($resp_msg);
185 }
186
187 package main;
188 use strict;
189 use utf8;
190
191 use Config::Simple;
192 use File::Path;
193
194 my $config_name = $ARGV[0] || 'not_found';
195
196 my %cfg;
197 my $config_path = ('/etc/mubot4fb/', $ENV{HOME} . '/.mubot4fb/', $ENV{PWD} . '/mubot4fb_');
198 foreach my $c ($config_path) {
199         my $config = $c . $config_name . '.conf';
200         Config::Simple->import_from($config, \%cfg) if (-e $config);
201 }
202 die 'missing config file' unless (keys %cfg);
203
204 die 'missing some config parameters should be defined (irc_server, fb_app_id, fb_app_secret, fb_access_code, fb_page_id fb_postback_url)'
205   if (!defined $cfg{'irc_server'}
206       || !defined $cfg{'fb_app_id'}
207       || !defined $cfg{'fb_app_secret'}
208       || !defined $cfg{'fb_access_code'}
209       || !defined $cfg{'fb_page_id'}
210       || !defined $cfg{'fb_postback_url'}
211     );
212
213 $cfg{irc_port} ||= 6667;
214 $cfg{irc_channels} ||= ['#mubot4fb'];
215 $cfg{irc_nick} ||= 'mubot4fb';
216 $cfg{irc_name}||= $cfg{irc_nick};
217 $cfg{irc_charset} ||= 'utf-8';
218 $cfg{database} ||= $ENV{HOME} . '/.mubot4fb/data/' . $config_name . '.sqlite';
219
220 my $bot = Mubot4FB->new(server => $cfg{'irc_server'},
221                         port => $cfg{'irc_port'},
222                         channels => $cfg{'irc_channels'},
223                         nick => $cfg{'irc_nick'},
224                         username => $cfg{'irc_name'},
225                         name => $cfg{'irc_name'},
226                         charset => $cfg{'irc_charset'},
227                         cfg => \%cfg)->run();
228
229 1;