]> git.localhorst.tv Git - alttp.git/blob - app/TwitchBot/TwitchChatBot.php
a3ffe2dbed0970ef60a29080b718b205dcfc0287
[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->randomMsg($channel);
76                 if (!$text) return;
77                 $this->tagChannelWrite($channel);
78                 $this->sendIRCMessage(IRCMessage::privmsg($channel->twitch_chat, $text));
79         }
80
81         private function getChatSetting(Channel $channel, $name, $default = null) {
82                 if (array_key_exists($name, $channel->chat_settings)) {
83                         return $channel->chat_settings[$name];
84                 }
85                 return $default;
86         }
87
88         private function getNotes(Channel $channel) {
89                 if (!isset($this->notes[$channel->id])) {
90                         $this->notes[$channel->id] = [
91                                 'last_read' => 0,
92                                 'last_special' => '',
93                                 'last_write' => time(),
94                                 'latest_msgs' => [],
95                                 'read_since_last_write' => 0,
96                                 'wait_msgs' => $this->randomWaitMsgs($channel),
97                                 'wait_time' => $this->randomWaitTime($channel),
98                         ];
99                 }
100                 return $this->notes[$channel->id];
101         }
102
103         private function getNote(Channel $channel, $name, $default = null) {
104                 $notes = $this->getNotes($channel);
105                 if (array_key_exists($name, $notes)) {
106                         return $notes[$name];
107                 }
108                 return $default;
109         }
110
111         private function setNote(Channel $channel, $name, $value) {
112                 $this->getNotes($channel);
113                 $this->notes[$channel->id][$name] = $value;
114         }
115
116         private function checkForGG(Channel $channel) {
117                 $notes = $this->getNotes($channel);
118                 $ggs = 0;
119                 foreach ($notes['latest_msgs'] as $text) {
120                         if (ChatLog::classify($text) == 'gg') {
121                                 ++$ggs;
122                         }
123                 }
124                 return $ggs > 2;
125         }
126
127         private function checkForGLHF(Channel $channel) {
128                 $notes = $this->getNotes($channel);
129                 $gls = 0;
130                 foreach ($notes['latest_msgs'] as $text) {
131                         if (ChatLog::classify($text) == 'gl') {
132                                 ++$gls;
133                         }
134                 }
135                 return $gls > 2;
136         }
137
138         private function checkForGreeting(Channel $channel) {
139                 $notes = $this->getNotes($channel);
140                 $his = 0;
141                 foreach ($notes['latest_msgs'] as $text) {
142                         if (ChatLog::classify($text) == 'hi') {
143                                 ++$his;
144                         }
145                 }
146                 return $his > 2;
147         }
148
149         private function checkForHype(Channel $channel) {
150                 $notes = $this->getNotes($channel);
151                 $hypes = 0;
152                 foreach ($notes['latest_msgs'] as $text) {
153                         if (ChatLog::classify($text) == 'hype') {
154                                 ++$hypes;
155                         }
156                 }
157                 return $hypes > 2;
158         }
159
160         private function checkForLaughter(Channel $channel) {
161                 $notes = $this->getNotes($channel);
162                 $lulz = 0;
163                 foreach ($notes['latest_msgs'] as $text) {
164                         if (ChatLog::classify($text) == 'lol') {
165                                 ++$lulz;
166                         }
167                 }
168                 return $lulz > 2;
169         }
170
171         private function checkForNumbers(Channel $channel) {
172                 $notes = $this->getNotes($channel);
173                 $numbers = 0;
174                 foreach ($notes['latest_msgs'] as $text) {
175                         if (is_numeric(trim($text))) {
176                                 ++$numbers;
177                         }
178                 }
179                 return $numbers > 2;
180         }
181
182         private function checkForPog(Channel $channel) {
183                 $notes = $this->getNotes($channel);
184                 $pogs = 0;
185                 foreach ($notes['latest_msgs'] as $text) {
186                         if (ChatLog::classify($text) == 'pog') {
187                                 ++$pogs;
188                         }
189                 }
190                 return $pogs > 2;
191         }
192
193         private function checkForSalute(Channel $channel) {
194                 $notes = $this->getNotes($channel);
195                 $o7s = 0;
196                 foreach ($notes['latest_msgs'] as $text) {
197                         if (ChatLog::classify($text) == 'o7') {
198                                 ++$o7s;
199                         }
200                 }
201                 return $o7s > 2;
202         }
203
204         private function contextualMsg(Channel $channel) {
205                 $last = $this->getNote($channel, 'last_special');
206                 if ($last != 'gg' && $this->checkForGG($channel)) {
207                         $this->setNote($channel, 'last_special', 'gg');
208                         return $this->randomOfClass($channel, 'gg');
209                 }
210                 if ($last != 'number' && $this->checkForNumbers($channel)) {
211                         $this->setNote($channel, 'last_special', 'number');
212                         return $this->randomContextualNumber($channel);
213                 }
214                 if ($last != 'lol' && $this->checkForLaughter($channel)) {
215                         $this->setNote($channel, 'last_special', 'lol');
216                         return $this->randomLaughter($channel);
217                 }
218                 if ($last != 'glhf' && $this->checkForGLHF($channel)) {
219                         $this->setNote($channel, 'last_special', 'glhf');
220                         return $this->randomOfClass($channel, 'gl');
221                 }
222                 if ($last != 'hi' && $this->checkForGreeting($channel)) {
223                         $this->setNote($channel, 'last_special', 'hi');
224                         return $this->randomOfClass($channel, 'hi');
225                 }
226                 if ($last != 'hype' && $this->checkForHype($channel)) {
227                         $this->setNote($channel, 'last_special', 'hype');
228                         return $this->randomOfClass($channel, 'hype');
229                 }
230                 if ($last != 'pog' && $this->checkForPog($channel)) {
231                         $this->setNote($channel, 'last_special', 'pog');
232                         return $this->randomOfClass($channel, 'pog');
233                 }
234                 if ($last != 'o7' && $this->checkForSalute($channel)) {
235                         $this->setNote($channel, 'last_special', 'o7');
236                         return $this->randomOfClass($channel, 'o7');
237                 }
238                 return false;
239         }
240
241         private function queryChatlog(Channel $channel) {
242                 return ChatLog::where('type', '=', 'chat')
243                         ->where('banned', '=', false)
244                         ->where('created_at', '<', now()->sub(1, 'day'))
245                         ->where(function ($query) use ($channel) {
246                                 $query->whereNull('detected_language');
247                                 $query->orWhereIn('detected_language', $channel->languages);
248                         })
249                         ->inRandomOrder();
250         }
251
252         private function randomContextualNumber(Channel $channel) {
253                 $notes = $this->getNotes($channel);
254                 $min = 100000;
255                 $max = 0;
256                 foreach ($notes['latest_msgs'] as $text) {
257                         if (is_numeric(trim($text))) {
258                                 $number = intval(trim($text));
259                                 $min = min($min, $number);
260                                 $max = max($max, $number);
261                         }
262                 }
263                 return random_int($min, $max);
264         }
265
266         private function randomOfClass(Channel $channel, $class) {
267                 $line = $this->queryChatlog($channel)
268                         ->where('classification', '=', $class)
269                         ->first();
270                 return $line->text_content;
271         }
272
273         private function randomLaughter(Channel $channel) {
274                 return Arr::random([
275                         ':tf:',
276                         '4Head',
277                         'CarlSmile',
278                         'CruW',
279                         'DendiFace',
280                         'EleGiggle',
281                         'GunRun',
282                         'heh',
283                         'Hhhehehe',
284                         'HypeLUL',
285                         'Jebaited',
286                         'Jebasted',
287                         'KEKW',
288                         'KEKHeim',
289                         'KKona',
290                         'KomodoHype',
291                         'MaxLOL',
292                         'MingLee',
293                         'lol',
294                         'LOL!',
295                         'LUL',
296                         'OneHand',
297                         'SeemsGood',
298                         'ShadyLulu',
299                         'SoonerLater',
300                         'SUBprise',
301                         'xD',
302                         'YouDontSay',
303                         $this->randomOfClass($channel, 'lol'),
304                 ]);
305         }
306
307         private function randomMsg(Channel $channel) {
308                 $line = $this->queryChatlog($channel)->first();
309                 return $line->text_content;
310         }
311
312         private function randomWaitMsgs(Channel $channel) {
313                 $min = $this->getChatSetting($channel, 'wait_msgs_min', 1);
314                 $max = $this->getChatSetting($channel, 'wait_msgs_max', 10);
315                 return random_int($min, $max);
316         }
317
318         private function randomWaitTime(Channel $channel) {
319                 $min = $this->getChatSetting($channel, 'wait_time_min', 1);
320                 $max = $this->getChatSetting($channel, 'wait_time_max', 900);
321                 return random_int($min, $max);
322         }
323
324         private function tagChannelRead(Channel $channel, IRCMessage $msg) {
325                 $this->getNotes($channel);
326                 $this->notes[$channel->id]['last_read'] = time();
327                 ++$this->notes[$channel->id]['read_since_last_write'];
328                 $this->notes[$channel->id]['latest_msgs'][] = $msg->getText();
329                 if (count($this->notes[$channel->id]['latest_msgs']) > 10) {
330                         array_shift($this->notes[$channel->id]['latest_msgs']);
331                 }
332         }
333
334         private function tagChannelWrite(Channel $channel) {
335                 $this->getNotes($channel);
336                 $this->notes[$channel->id]['last_write'] = time();
337                 $this->notes[$channel->id]['read_since_last_write'] = 0;
338                 $this->notes[$channel->id]['wait_msgs'] = $this->randomWaitMsgs($channel);
339                 $this->notes[$channel->id]['wait_time'] = $this->randomWaitTime($channel);
340         }
341
342         private $channels;
343         private $notes = [];
344
345 }