ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-Knuddels/eg/markovd
Revision: 1.51
Committed: Tue Feb 1 01:07:21 2005 UTC (21 years, 7 months ago) by root
Branch: MAIN
Changes since 1.50: +7 -4 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 #!/opt/bin/perl
2
3 use strict;
4
5 package markov;
6
7 sub new {
8 my ($class, @arg) = @_;
9
10 bless {
11 longest => 10,
12 s_beg => [], # arbitarry "unique" start symbol
13 s_end => [], # arbitrary "unique" end symbol
14 tree => {},
15 @arg,
16 }, $class;
17 }
18
19 sub simplify {
20 local $_ = lc shift;
21 y/aeiouüöä//d;
22 s/ß/ss/g;
23 y/a-z\000//cd;
24 $_;
25 }
26
27 sub seed {
28 my ($self, $symbols) = @_;
29
30 my @sym = (@$symbols, $self->{s_end});
31 my @seq = $self->{s_beg};
32 my $tree = $self->{tree};
33
34 while () {
35 my $next = shift @sym
36 or last;
37
38 shift @seq while @seq > $self->{longest};
39
40 for (1 .. @seq) {
41 my $node = $tree->{simplify join "\0", @seq[-$_ .. -1]} ||= {};
42 $node->{$next}++;
43 $node->{""}++;
44 }
45
46 push @seq, $next;
47 }
48 }
49
50 sub complete {
51 my ($self, $symbols, $prob) = @_;
52
53 my $tree = $self->{tree};
54 my @res;
55 my @sym = @$symbols;
56
57 # find starting sequence
58 shift @sym while @sym && !$tree->{simplify join "\0", @sym};
59
60 @res = @sym;
61 @sym = $self->{s_beg} unless @sym;
62
63 #use Dumpvalue; Dumpvalue->new (compactDump => 1, veryCompact => 1, quoteHighBit => 1, tick => '"')->dumpValue ($self);
64
65 outer:
66 while () {
67 my $node = $tree->{simplify join "\0", @sym};
68
69 if ($node) {
70 my $sel = rand $node->{""};
71 keys %$node;
72
73 while (my ($k, $v) = each %$node) {
74 if (length $k and ($sel -= $v) < 0) {
75 last outer if $k eq $self->{s_end};
76
77 push @sym, $k;
78 push @res, $k;
79
80 $prob->{$k} = $v / $node->{""};
81
82 next outer;
83 }
84 }
85
86 die "FATAL: internal error";
87 } else {
88 shift @sym;
89 @sym
90 or die "FATAL: empty prefix (ENOTUNDERSTOOD)";
91 }
92 }
93
94 @res
95 }
96
97 package main;
98
99 use Socket;
100 use IO::Socket::INET;
101
102 use YAML;
103 use Encode;
104 use Event;
105 use Net::Knuddels;
106 use List::Util;
107 use Time::HiRes;
108
109 my @CHANNELS = (
110 'Flirt',
111 'Flirt Private',
112 'Singles 11-14',
113 'Singles 15-17',
114
115 'Singles 11-14 2',
116 'Singles 15-17 2',
117 'Flirt 2',
118 'Flirt Private 2',
119 'Singles 11-14 3',
120 'Singles 15-17 3',
121 'Flirt 3',
122 'Flirt Private 3',
123 'Singles 11-14 4',
124 'Singles 15-17 4',
125 'Flirt 4',
126 'Flirt Private 4',
127 'Singles 11-14 5',
128 'Singles 15-17 5',
129 'Flirt 5',
130 'Flirt Private 5',
131 'Singles 11-14 6',
132 'Singles 15-17 6',
133 'Flirt 6',
134 'Flirt Private 6',
135 'Singles 11-14 7',
136 'Singles 15-17 7',
137 'Flirt 7',
138 'Flirt Private 7',
139 'Singles 11-14 8',
140 'Singles 15-17 8',
141 'Flirt 8',
142 'Flirt Private 8',
143 );
144
145 my $logdir = "logs";
146
147 my $Knick = "ich bin suess";
148 my $Kpass = "qwerty";
149
150 my $client;
151
152 my $seed = [grep $_, split /\n/, do { open my $fh, "<:utf8", "markovbot.txt"; local $/; <$fh> } ];
153
154 my $fwd = new markov longest => 2;
155 my $rev = new markov longest => 2;
156
157 my %freq;
158 my $word_cnt;
159
160 sub word {
161 $_[0] =~ /(\w+)/ ? lc $1 : ();
162 }
163
164 sub seed_msg {
165 my $msg = $_[0];
166
167 return if $msg =~ /leck|fick|möse|mose|moese|uschi|usci|ushi|fikk|scheide|vagina
168 |bums|wichs|wix|popp|popen|fuck|dreck|laber|saugen|pussy|\bsuck\b|lutsch
169 |knallen|schlecken
170 |\bblas|piss|schluck|fingern|spritz|\bloch|\bsteck|vögel|voegel|vogel
171 |hure|strich|sklave|handschell|slave|perver|befehl|stöhn|dildo
172 |stengel|penis|schwanz|pimmel|steifen|ejak|sperma|\btitt|\bass|\bsack|\bsaft\b|\beier\b|willy
173 |\bcs\b|\bts\b|\brs\b|\bicq\b
174 |cam\b|\bmsn\b|\bbot\b|\bchat.*bot\b|tanga|schlafen|sex|\bsau\b
175 |intim|arsch|fotze|dumm|schnauze|klappe|rasiert|fresse|maul\b|\bmaul|rosett?e
176 |zieh.*aus|nackt|geile|feucht|willig
177 |sätz|saetz|setze|\bsatz
178 |sc?h?wul|schwuchtel|transe|transv|schlampe|tussi|nutte|mädel
179 |admin|nachgeburt
180 |(?-i:[A-Z]{4,})
181 |\S{20,}
182 /xi;
183
184 my @msg = $msg =~ m/(\S+)/g;
185 #my @msg = split /\b/, $msg;
186
187 $freq{word $_}++ for @msg;
188 $word_cnt += @msg;
189
190 $fwd->seed (\@msg);
191 $rev->seed ([reverse @msg]);
192 }
193
194 for (@$seed) {
195 seed_msg $_;
196 }
197
198 my %grammar_reply = qw(
199 ich du
200 du ich
201 mir dir
202 dir mir
203 mein dein
204 dein mein
205 deine meine
206 meine deine
207 deiner meiner
208 meiner deiner
209 frau mann
210 mädel junge
211 mädchen junge
212 girls boys
213 boys girls
214 huhu hi
215 hi hi
216 hallo hi
217 typen mädels
218 bye bye
219 ciao bye
220 );
221
222 sub gen_reply {
223 my ($msg) = @_;
224
225 my @msg = $msg =~ /(\S+)/g;
226
227 my @key = map {
228 my $word = word $_;
229
230 $freq{$word} < $word_cnt * 0.003
231 && $freq{$word}
232 && 2 <= length $word
233 ? $word
234 : ()
235 } @msg;
236
237 my @srch = (("") x 5, @key, map {
238 my $word = word $_;
239
240 $grammar_reply{$word} || ()
241 } @msg);
242
243 my $reply;
244 my $best = -1;
245 my $idx;
246
247 #warn "KEY<@key> SRCH<@srch>\n";#d#
248
249 for (1..200) {
250 my $prob = {};
251
252 my @r = $rev->complete (
253 [reverse $fwd->complete (
254 [
255 $srch[++$idx % @srch]
256 ],
257 $prob
258 ) ], $prob
259 );
260
261 # my $b = (rand 0.02 / (@r + 1))
262 # + (@key ? (List::Util::sum map $prob->{$_} || 1, @key) / @key : 0);
263 #
264 # $b += 0.2 if @r < 3;
265
266 $b = @r ** 0.2 * (rand)
267 + (@key ? (List::Util::sum map $prob->{$_} || 1, @key) / @key : 0);
268
269 #my $b = (List::Util::sum map $freq{word $_}, @r) / (@r ** 3 * $word_cnt);
270
271 ($reply, $best) = ((join " ", reverse @r), $b) if $b > $best;
272 }
273
274 $reply;
275 }
276
277 while ($ENV{DEBUG}) {
278 my $r = gen_reply scalar <>;
279 print "$r\n\n";
280 }
281
282 Event->signal (signal => "INT", cb => sub { Event::unloop(-1) });
283
284 Event->timer (after => 60, interval => 60, cb => sub {
285 open my $fh, ">:utf8", "markovbot.txt~"
286 or return;
287 print $fh join "\n", @$seed;
288 close $fh;
289 rename "markovbot.txt~", "markovbot.txt";
290 });
291
292 sub logit {
293 my ($msg, $file, $src, $dst, $room) = @_;
294
295 mkdir $logdir;
296 my $fh;
297
298 unless (open $fh, ">>:utf8", Encode::encode_utf8 "$logdir/$file") {
299 warn "Couldn't open for appending $logdir/$src: $!\n";
300 return;
301 }
302
303 print $fh "$room\t$src\t$dst\t$msg\n";
304 }
305
306 ####################################################################################
307 ########################## MAIN START ##############################################
308 ####################################################################################
309
310 $client = new Net::Knuddels::Client
311 PeerAddr => "213.61.5.150:2710",
312 command_wait => sub {
313 my ($client, $wait) = @_;
314 Event->timer (after => $wait, cb => sub { $client->command_cb });
315 };
316
317 Event->io (
318 fd => $client->fh,
319 poll => 'r',
320 cb => sub {
321 $client->ready
322 or $_[0]->w->cancel;
323 });
324
325 $client->login;
326
327 $client->register (dialog => sub {
328 use Dumpvalue;
329 print "---\n";
330 Dumpvalue->new (compactDump => 1, veryCompact => 1, quoteHighBit => 1, tick => '"')->dumpValue ([@_]);
331 Event::unloop(-1) if grep /Falsches.*Passwort/i, @_;
332 });
333
334 $client->register (login => sub {
335 Event->timer (after => 0, interval => 3, cb => sub {
336 $client->enter_room (shift @CHANNELS, $Knick, $Kpass);
337 });
338 });
339
340 $client->register (msg_room => sub {
341 my ($room, $user, $msg) = @_;
342 });
343
344 my @queue;
345
346 Event->timer (interval => 1, cb => sub {
347 my $msg = shift @queue
348 or return;
349
350 logit ($msg->[2], $msg->[0], $Knick, $msg->[0], $msg->[1]);
351 $client->send_priv_msg (@$msg);
352 });
353
354 my $some_room;
355
356 $client->register (room_info => sub {
357 print "JOIN ROOM: $_[0]\n";
358 $some_room = $_[0];
359 });
360
361 Event->timer (after => 60, interval => 60, cb => sub {
362 $client->send_priv_msg ("James", $some_room, "/knuschel");
363 });
364
365 my %next_time;
366
367 $client->register (msg_priv_nondup => sub {
368 my ($room, $src, $dst, $msg) = @_;
369
370 my $NOW = Time::HiRes::time;
371
372 $msg =~ s/\260[^\260]*\260//g;
373
374 print "($room) $src >> $msg\n";
375 logit ($msg, $src, $src, $dst, $room);
376
377 return if $next_time{$src} > time; # do not talk unnaturally often
378
379 my $reply = gen_reply $msg;
380
381 push @$seed, $msg;
382 #seed_msg $msg;#d#
383
384 my $delay = 2 + 30 * (rand) ** 5 + 0.2 * length $reply;
385 $next_time{$src} = time + $delay;
386
387 print "($room) $src << $reply ($delay)\n";
388
389 Event->timer (at => $NOW + $delay, cb => sub {
390 push @queue, [$src, $room, $reply];
391 });
392 });
393
394 Event::loop;
395