]> git.localhorst.tv Git - alttp.git/blob - app/TwitchBot/TwitchChatBot.php
random chat buttons
[alttp.git] / app / TwitchBot / TwitchChatBot.php
1 <?php
2
3 namespace App\TwitchBot;
4
5 use App\Models\Channel;
6 use App\Models\ChatLog;
7 use Illuminate\Support\Arr;
8 use Illuminate\Support\Str;
9
10 class TwitchChatBot extends TwitchBot {
11
12         public function __construct() {
13                 parent::__construct('horstiebot');
14                 $this->updateChannels();
15                 $this->startTimer();
16                 $this->listenCommands();
17         }
18
19         public function joinChannels() {
20                 $this->getLogger()->info('joining channels');
21                 $names = [];
22                 foreach ($this->channels as $channel) {
23                         $names[] = $channel->twitch_chat;
24                 }
25                 $chunks = array_chunk($names, 10);
26                 foreach ($chunks as $chunk) {
27                         $this->sendIRCMessage(IRCMessage::join($chunk));
28                 }
29         }
30
31         public function logMessage(IRCMessage $msg) {
32                 $channel = $this->getMessageChannel($msg);
33                 if ($channel && !$channel->join) {
34                         $msg->log();
35                 }
36         }
37
38         public function handlePrivMsg(IRCMessage $msg) {
39                 if ($msg->nick == 'horstiebot') return;
40                 $channel = $this->getMessageChannel($msg);
41                 if (!$channel) return;
42                 $this->tagChannelRead($channel, $msg);
43         }
44
45
46         private function startTimer() {
47                 $this->getLoop()->addPeriodicTimer(1, function () {
48                         if (!$this->isReady()) return;
49                         foreach ($this->channels as $channel) {
50                                 $this->decideSend($channel);
51                         }
52                 });
53                 $this->getLoop()->addPeriodicTimer(60, function () {
54                         $this->updateChannels();
55                 });
56         }
57
58         private function updateChannels() {
59                 $this->channels = Channel::where('twitch_chat', '!=', '')->where('chat', '=', true)->get();
60         }
61
62         private function decideSend(Channel $channel) {
63                 $notes = $this->getNotes($channel);
64                 if ($notes['read_since_last_write'] < $notes['wait_msgs']) {
65                         return;
66                 }
67                 if (time() - $notes['last_write'] < $notes['wait_time']) {
68                         return;
69                 }
70                 if ($notes['read_since_last_write'] == $notes['wait_msgs'] && time() - $notes['last_read'] < 3) {
71                         // don't immediately respond if we crossed the msg threshold last
72                         return;
73                 }
74                 $text = $this->contextualMsg($channel);
75                 if (!$text) $text = $this->randomChat($channel);
76                 if (!$text) return;
77                 $this->tagChannelWrite($channel);
78                 $this->sendIRCMessage(IRCMessage::privmsg($channel->twitch_chat, $text));
79         }
80
81         private function getNotes(Channel $channel) {
82                 if (!isset($this->notes[$channel->id])) {
83                         $this->notes[$channel->id] = [
84                                 'last_read' => 0,
85                                 'last_special' => '',
86                                 'last_write' => time(),
87                                 'latest_msgs' => [],
88                                 'read_since_last_write' => 0,
89                                 'wait_msgs' => $this->randomWaitMsgs($channel),
90                                 'wait_time' => $this->randomWaitTime($channel),
91                         ];
92                 }
93                 return $this->notes[$channel->id];
94         }
95
96         private function getNote(Channel $channel, $name, $default = null) {
97                 $notes = $this->getNotes($channel);
98                 if (array_key_exists($name, $notes)) {
99                         return $notes[$name];
100                 }
101                 return $default;
102         }
103
104         private function setNote(Channel $channel, $name, $value) {
105                 $this->getNotes($channel);
106                 $this->notes[$channel->id][$name] = $value;
107         }
108
109         private function checkForGG(Channel $channel) {
110                 $notes = $this->getNotes($channel);
111                 $ggs = 0;
112                 foreach ($notes['latest_msgs'] as $text) {
113                         if (ChatLog::classify($text) == 'gg') {
114                                 ++$ggs;
115                         }
116                 }
117                 return $ggs > 2;
118         }
119
120         private function checkForGLHF(Channel $channel) {
121                 $notes = $this->getNotes($channel);
122                 $gls = 0;
123                 foreach ($notes['latest_msgs'] as $text) {
124                         if (ChatLog::classify($text) == 'gl') {
125                                 ++$gls;
126                         }
127                 }
128                 return $gls > 2;
129         }
130
131         private function checkForGreeting(Channel $channel) {
132                 $notes = $this->getNotes($channel);
133                 $his = 0;
134                 foreach ($notes['latest_msgs'] as $text) {
135                         if (ChatLog::classify($text) == 'hi') {
136                                 ++$his;
137                         }
138                 }
139                 return $his > 2;
140         }
141
142         private function checkForHype(Channel $channel) {
143                 $notes = $this->getNotes($channel);
144                 $hypes = 0;
145                 foreach ($notes['latest_msgs'] as $text) {
146                         if (ChatLog::classify($text) == 'hype') {
147                                 ++$hypes;
148                         }
149                 }
150                 return $hypes > 2;
151         }
152
153         private function checkForLaughter(Channel $channel) {
154                 $notes = $this->getNotes($channel);
155                 $lulz = 0;
156                 foreach ($notes['latest_msgs'] as $text) {
157                         if (ChatLog::classify($text) == 'lol') {
158                                 ++$lulz;
159                         }
160                 }
161                 return $lulz > 2;
162         }
163
164         private function checkForNumbers(Channel $channel) {
165                 $notes = $this->getNotes($channel);
166                 $numbers = 0;
167                 foreach ($notes['latest_msgs'] as $text) {
168                         if (is_numeric(trim($text))) {
169                                 ++$numbers;
170                         }
171                 }
172                 return $numbers > 2;
173         }
174
175         private function checkForPog(Channel $channel) {
176                 $notes = $this->getNotes($channel);
177                 $pogs = 0;
178                 foreach ($notes['latest_msgs'] as $text) {
179                         if (ChatLog::classify($text) == 'pog') {
180                                 ++$pogs;
181                         }
182                 }
183                 return $pogs > 2;
184         }
185
186         private function checkForSalute(Channel $channel) {
187                 $notes = $this->getNotes($channel);
188                 $o7s = 0;
189                 foreach ($notes['latest_msgs'] as $text) {
190                         if (ChatLog::classify($text) == 'o7') {
191                                 ++$o7s;
192                         }
193                 }
194                 return $o7s > 2;
195         }
196
197         private function contextualMsg(Channel $channel) {
198                 $last = $this->getNote($channel, 'last_special');
199                 if ($last != 'gg' && $this->checkForGG($channel)) {
200                         $this->setNote($channel, 'last_special', 'gg');
201                         return $channel->randomOfClass('gg');
202                 }
203                 if ($last != 'number' && $this->checkForNumbers($channel)) {
204                         $this->setNote($channel, 'last_special', 'number');
205                         return $this->randomContextualNumber($channel);
206                 }
207                 if ($last != 'lol' && $this->checkForLaughter($channel)) {
208                         $this->setNote($channel, 'last_special', 'lol');
209                         return $this->randomLaughter($channel);
210                 }
211                 if ($last != 'glhf' && $this->checkForGLHF($channel)) {
212                         $this->setNote($channel, 'last_special', 'glhf');
213                         return $channel->randomOfClass('gl');
214                 }
215                 if ($last != 'hi' && $this->checkForGreeting($channel)) {
216                         $this->setNote($channel, 'last_special', 'hi');
217                         return $channel->randomOfClass('hi');
218                 }
219                 if ($last != 'hype' && $this->checkForHype($channel)) {
220                         $this->setNote($channel, 'last_special', 'hype');
221                         return $channel->randomOfClass('hype');
222                 }
223                 if ($last != 'pog' && $this->checkForPog($channel)) {
224                         $this->setNote($channel, 'last_special', 'pog');
225                         return $channel->randomOfClass('pog');
226                 }
227                 if ($last != 'o7' && $this->checkForSalute($channel)) {
228                         $this->setNote($channel, 'last_special', 'o7');
229                         return $channel->randomOfClass('o7');
230                 }
231                 return false;
232         }
233
234         private function randomChat(Channel $channel) {
235                 $line = $channel->queryChatlog()
236                         ->whereIn('classification', ['hi', 'hype', 'lol', 'pog', 'unclassified'])
237                         ->first();
238                 return $line->text_content;
239         }
240
241         private function randomContextualNumber(Channel $channel) {
242                 $notes = $this->getNotes($channel);
243                 $min = 100000;
244                 $max = 0;
245                 foreach ($notes['latest_msgs'] as $text) {
246                         if (is_numeric(trim($text))) {
247                                 $number = intval(trim($text));
248                                 $min = min($min, $number);
249                                 $max = max($max, $number);
250                         }
251                 }
252                 return random_int($min, $max);
253         }
254
255         private function randomLaughter(Channel $channel) {
256                 return Arr::random([
257                         ':tf:',
258                         '4Head',
259                         'CarlSmile',
260                         'CruW',
261                         'DendiFace',
262                         'EleGiggle',
263                         'GunRun',
264                         'heh',
265                         'Hhhehehe',
266                         'HypeLUL',
267                         'Jebaited',
268                         'Jebasted',
269                         'KEKW',
270                         'KEKHeim',
271                         'KKona',
272                         'KomodoHype',
273                         'MaxLOL',
274                         'MingLee',
275                         'lol',
276                         'LOL!',
277                         'LUL',
278                         'OneHand',
279                         'SeemsGood',
280                         'ShadyLulu',
281                         'SoonerLater',
282                         'SUBprise',
283                         'xD',
284                         'YouDontSay',
285                         $channel->randomOfClass('lol'),
286                 ]);
287         }
288
289         private function randomMsg(Channel $channel) {
290                 $line = $channel->queryChatlog()->first();
291                 return $line->text_content;
292         }
293
294         private function randomWaitMsgs(Channel $channel) {
295                 $min = $channel->getChatSetting('wait_msgs_min', 1);
296                 $max = $channel->getChatSetting('wait_msgs_max', 10);
297                 return random_int($min, $max);
298         }
299
300         private function randomWaitTime(Channel $channel) {
301                 $min = $channel->getChatSetting('wait_time_min', 1);
302                 $max = $channel->getChatSetting('wait_time_max', 900);
303                 return random_int($min, $max);
304         }
305
306         private function tagChannelRead(Channel $channel, IRCMessage $msg) {
307                 $this->getNotes($channel);
308                 $this->notes[$channel->id]['last_read'] = time();
309                 ++$this->notes[$channel->id]['read_since_last_write'];
310                 if (!ChatLog::isKnownBot($msg->nick) && !ChatLog::spammyText($msg->getText())) {
311                         $this->notes[$channel->id]['latest_msgs'][] = $msg->getText();
312                         if (count($this->notes[$channel->id]['latest_msgs']) > 10) {
313                                 array_shift($this->notes[$channel->id]['latest_msgs']);
314                         }
315                 }
316                 if ($this->isDirectedAtMe($msg->getText()) && $this->shouldRespond($channel)) {
317                         $this->notes[$channel->id]['wait_msgs'] = 0;
318                         $this->notes[$channel->id]['wait_time'] = 0;
319                 }
320         }
321
322         private function tagChannelWrite(Channel $channel) {
323                 $this->getNotes($channel);
324                 $this->notes[$channel->id]['last_write'] = time();
325                 $this->notes[$channel->id]['read_since_last_write'] = 0;
326                 $this->notes[$channel->id]['wait_msgs'] = $this->randomWaitMsgs($channel);
327                 $this->notes[$channel->id]['wait_time'] = $this->randomWaitTime($channel);
328         }
329
330         private function isDirectedAtMe($raw_text) {
331                 $text = strtolower($raw_text);
332                 if (strpos($text, 'horstie') !== false) {
333                         return true;
334                 }
335                 return false;
336         }
337
338         private function shouldRespond(Channel $channel) {
339                 $setting = $channel->getChatSetting('respond', 'yes');
340                 if ($setting == 'yes') {
341                         return true;
342                 }
343                 if ($setting == '50') {
344                         return random_int(0, 1);
345                 }
346                 return false;
347         }
348
349         private $channels;
350         private $notes = [];
351
352 }