]> git.localhorst.tv Git - alttp.git/blob - app/TwitchBot/TwitchChatBot.php
use classified messages
[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 contextualMsg(Channel $channel) {
194                 $last = $this->getNote($channel, 'last_special');
195                 if ($last != 'gg' && $this->checkForGG($channel)) {
196                         $this->setNote($channel, 'last_special', 'gg');
197                         return $this->randomOfClass($channel, 'gg');
198                 }
199                 if ($last != 'number' && $this->checkForNumbers($channel)) {
200                         $this->setNote($channel, 'last_special', 'number');
201                         return $this->randomContextualNumber($channel);
202                 }
203                 if ($last != 'lol' && $this->checkForLaughter($channel)) {
204                         $this->setNote($channel, 'last_special', 'lol');
205                         return $this->randomLaughter($channel);
206                 }
207                 if ($last != 'glhf' && $this->checkForGLHF($channel)) {
208                         $this->setNote($channel, 'last_special', 'glhf');
209                         return $this->randomOfClass($channel, 'gl');
210                 }
211                 if ($last != 'hi' && $this->checkForGreeting($channel)) {
212                         $this->setNote($channel, 'last_special', 'hi');
213                         return $this->randomOfClass($channel, 'hi');
214                 }
215                 if ($last != 'hype' && $this->checkForHype($channel)) {
216                         $this->setNote($channel, 'last_special', 'hype');
217                         return $this->randomOfClass($channel, 'hype');
218                 }
219                 if ($last != 'pog' && $this->checkForPog($channel)) {
220                         $this->setNote($channel, 'last_special', 'pog');
221                         return $this->randomOfClass($channel, 'pog');
222                 }
223                 return false;
224         }
225
226         private function queryChatlog(Channel $channel) {
227                 return ChatLog::where('type', '=', 'chat')
228                         ->where('banned', '=', false)
229                         ->where('created_at', '<', now()->sub(1, 'day'))
230                         ->where(function ($query) use ($channel) {
231                                 $query->whereNull('detected_language');
232                                 $query->orWhereIn('detected_language', $channel->languages);
233                         })
234                         ->inRandomOrder();
235         }
236
237         private function randomContextualNumber(Channel $channel) {
238                 $notes = $this->getNotes($channel);
239                 $min = 100000;
240                 $max = 0;
241                 foreach ($notes['latest_msgs'] as $text) {
242                         if (is_numeric(trim($text))) {
243                                 $number = intval(trim($text));
244                                 $min = min($min, $number);
245                                 $max = max($max, $number);
246                         }
247                 }
248                 return random_int($min, $max);
249         }
250
251         private function randomOfClass(Channel $channel, $class) {
252                 $line = $this->queryChatlog($channel)
253                         ->where('classification', '=', $class)
254                         ->first();
255                 return $line->text_content;
256         }
257
258         private function randomLaughter(Channel $channel) {
259                 return Arr::random([
260                         ':tf:',
261                         '4Head',
262                         'CarlSmile',
263                         'CruW',
264                         'DendiFace',
265                         'EleGiggle',
266                         'GunRun',
267                         'heh',
268                         'Hhhehehe',
269                         'HypeLUL',
270                         'Jebaited',
271                         'Jebasted',
272                         'KEKW',
273                         'KEKHeim',
274                         'KKona',
275                         'KomodoHype',
276                         'MaxLOL',
277                         'MingLee',
278                         'lol',
279                         'LOL!',
280                         'LUL',
281                         'OneHand',
282                         'SeemsGood',
283                         'ShadyLulu',
284                         'SoonerLater',
285                         'SUBprise',
286                         'xD',
287                         'YouDontSay',
288                         $this->randomOfClass($channel, 'lol'),
289                 ]);
290         }
291
292         private function randomMsg(Channel $channel) {
293                 $line = $this->queryChatlog($channel)->first();
294                 return $line->text_content;
295         }
296
297         private function randomWaitMsgs(Channel $channel) {
298                 $min = $this->getChatSetting($channel, 'wait_msgs_min', 1);
299                 $max = $this->getChatSetting($channel, 'wait_msgs_max', 10);
300                 return random_int($min, $max);
301         }
302
303         private function randomWaitTime(Channel $channel) {
304                 $min = $this->getChatSetting($channel, 'wait_time_min', 1);
305                 $max = $this->getChatSetting($channel, 'wait_time_max', 900);
306                 return random_int($min, $max);
307         }
308
309         private function tagChannelRead(Channel $channel, IRCMessage $msg) {
310                 $this->getNotes($channel);
311                 $this->notes[$channel->id]['last_read'] = time();
312                 ++$this->notes[$channel->id]['read_since_last_write'];
313                 $this->notes[$channel->id]['latest_msgs'][] = $msg->getText();
314                 if (count($this->notes[$channel->id]['latest_msgs']) > 10) {
315                         array_shift($this->notes[$channel->id]['latest_msgs']);
316                 }
317         }
318
319         private function tagChannelWrite(Channel $channel) {
320                 $this->getNotes($channel);
321                 $this->notes[$channel->id]['last_write'] = time();
322                 $this->notes[$channel->id]['read_since_last_write'] = 0;
323                 $this->notes[$channel->id]['wait_msgs'] = $this->randomWaitMsgs($channel);
324                 $this->notes[$channel->id]['wait_time'] = $this->randomWaitTime($channel);
325         }
326
327         private $channels;
328         private $notes = [];
329
330 }